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.
Files changed (267) hide show
  1. pyglet/__init__.py +67 -61
  2. pyglet/__init__.pyi +15 -8
  3. pyglet/app/__init__.py +22 -13
  4. pyglet/app/async_app.py +212 -0
  5. pyglet/app/base.py +2 -1
  6. pyglet/app/{xlib.py → linux.py} +3 -3
  7. pyglet/config/__init__.py +101 -0
  8. pyglet/config/gl/__init__.py +30 -0
  9. pyglet/config/gl/egl.py +120 -0
  10. pyglet/config/gl/macos.py +262 -0
  11. pyglet/config/gl/windows.py +267 -0
  12. pyglet/config/gl/x11.py +142 -0
  13. pyglet/customtypes.py +43 -2
  14. pyglet/display/__init__.py +8 -6
  15. pyglet/display/base.py +3 -63
  16. pyglet/display/cocoa.py +12 -17
  17. pyglet/display/emscripten.py +39 -0
  18. pyglet/display/headless.py +23 -30
  19. pyglet/display/wayland.py +157 -0
  20. pyglet/display/win32.py +4 -17
  21. pyglet/display/xlib.py +19 -27
  22. pyglet/display/xlib_vidmoderestore.py +2 -2
  23. pyglet/enums.py +183 -0
  24. pyglet/event.py +0 -1
  25. pyglet/experimental/geoshader_sprite.py +15 -13
  26. pyglet/experimental/hidraw.py +6 -15
  27. pyglet/experimental/multitexture_sprite.py +31 -19
  28. pyglet/experimental/particles.py +13 -35
  29. pyglet/font/__init__.py +251 -85
  30. pyglet/font/base.py +116 -61
  31. pyglet/font/dwrite/__init__.py +349 -204
  32. pyglet/font/dwrite/dwrite_lib.py +27 -5
  33. pyglet/font/fontconfig.py +14 -6
  34. pyglet/font/freetype.py +138 -87
  35. pyglet/font/freetype_lib.py +19 -0
  36. pyglet/font/group.py +179 -0
  37. pyglet/font/harfbuzz/__init__.py +3 -3
  38. pyglet/font/pyodide_js.py +310 -0
  39. pyglet/font/quartz.py +319 -126
  40. pyglet/font/ttf.py +45 -3
  41. pyglet/font/user.py +14 -19
  42. pyglet/font/win32.py +45 -21
  43. pyglet/graphics/__init__.py +8 -787
  44. pyglet/graphics/allocation.py +115 -1
  45. pyglet/graphics/api/__init__.py +77 -0
  46. pyglet/graphics/api/base.py +299 -0
  47. pyglet/graphics/api/gl/__init__.py +58 -0
  48. pyglet/graphics/api/gl/base.py +24 -0
  49. pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
  50. pyglet/graphics/api/gl/cocoa/context.py +76 -0
  51. pyglet/graphics/api/gl/context.py +391 -0
  52. pyglet/graphics/api/gl/default_shaders.py +0 -0
  53. pyglet/graphics/api/gl/draw.py +627 -0
  54. pyglet/graphics/api/gl/egl/__init__.py +0 -0
  55. pyglet/graphics/api/gl/egl/context.py +92 -0
  56. pyglet/graphics/api/gl/enums.py +76 -0
  57. pyglet/graphics/api/gl/framebuffer.py +315 -0
  58. pyglet/graphics/api/gl/gl.py +5463 -0
  59. pyglet/graphics/api/gl/gl_info.py +188 -0
  60. pyglet/graphics/api/gl/global_opengl.py +226 -0
  61. pyglet/{gl → graphics/api/gl}/lib.py +34 -18
  62. pyglet/graphics/api/gl/shader.py +1476 -0
  63. pyglet/graphics/api/gl/shapes.py +55 -0
  64. pyglet/graphics/api/gl/sprite.py +102 -0
  65. pyglet/graphics/api/gl/state.py +219 -0
  66. pyglet/graphics/api/gl/text.py +190 -0
  67. pyglet/graphics/api/gl/texture.py +1526 -0
  68. pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
  69. pyglet/graphics/api/gl/vertexdomain.py +751 -0
  70. pyglet/graphics/api/gl/win32/__init__.py +0 -0
  71. pyglet/graphics/api/gl/win32/context.py +108 -0
  72. pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
  73. pyglet/graphics/api/gl/xlib/__init__.py +0 -0
  74. pyglet/graphics/api/gl/xlib/context.py +174 -0
  75. pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
  76. pyglet/graphics/api/gl1/__init__.py +0 -0
  77. pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
  78. pyglet/graphics/api/gl2/__init__.py +0 -0
  79. pyglet/graphics/api/gl2/buffer.py +320 -0
  80. pyglet/graphics/api/gl2/draw.py +600 -0
  81. pyglet/graphics/api/gl2/global_opengl.py +122 -0
  82. pyglet/graphics/api/gl2/shader.py +200 -0
  83. pyglet/graphics/api/gl2/shapes.py +51 -0
  84. pyglet/graphics/api/gl2/sprite.py +79 -0
  85. pyglet/graphics/api/gl2/text.py +175 -0
  86. pyglet/graphics/api/gl2/vertexdomain.py +364 -0
  87. pyglet/graphics/api/webgl/__init__.py +233 -0
  88. pyglet/graphics/api/webgl/buffer.py +302 -0
  89. pyglet/graphics/api/webgl/context.py +234 -0
  90. pyglet/graphics/api/webgl/draw.py +590 -0
  91. pyglet/graphics/api/webgl/enums.py +76 -0
  92. pyglet/graphics/api/webgl/framebuffer.py +360 -0
  93. pyglet/graphics/api/webgl/gl.py +1537 -0
  94. pyglet/graphics/api/webgl/gl_info.py +130 -0
  95. pyglet/graphics/api/webgl/shader.py +1346 -0
  96. pyglet/graphics/api/webgl/shapes.py +92 -0
  97. pyglet/graphics/api/webgl/sprite.py +102 -0
  98. pyglet/graphics/api/webgl/state.py +227 -0
  99. pyglet/graphics/api/webgl/text.py +187 -0
  100. pyglet/graphics/api/webgl/texture.py +1227 -0
  101. pyglet/graphics/api/webgl/vertexarray.py +54 -0
  102. pyglet/graphics/api/webgl/vertexdomain.py +616 -0
  103. pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
  104. pyglet/{image → graphics}/atlas.py +33 -32
  105. pyglet/graphics/base.py +10 -0
  106. pyglet/graphics/buffer.py +245 -0
  107. pyglet/graphics/draw.py +578 -0
  108. pyglet/graphics/framebuffer.py +26 -0
  109. pyglet/graphics/instance.py +178 -69
  110. pyglet/graphics/shader.py +267 -1553
  111. pyglet/graphics/state.py +83 -0
  112. pyglet/graphics/texture.py +703 -0
  113. pyglet/graphics/vertexdomain.py +695 -538
  114. pyglet/gui/ninepatch.py +10 -10
  115. pyglet/gui/widgets.py +120 -10
  116. pyglet/image/__init__.py +20 -1973
  117. pyglet/image/animation.py +12 -12
  118. pyglet/image/base.py +730 -0
  119. pyglet/image/codecs/__init__.py +9 -0
  120. pyglet/image/codecs/bmp.py +53 -30
  121. pyglet/image/codecs/dds.py +53 -31
  122. pyglet/image/codecs/gdiplus.py +38 -14
  123. pyglet/image/codecs/gdkpixbuf2.py +0 -2
  124. pyglet/image/codecs/js_image.py +99 -0
  125. pyglet/image/codecs/ktx2.py +161 -0
  126. pyglet/image/codecs/pil.py +1 -1
  127. pyglet/image/codecs/png.py +1 -1
  128. pyglet/image/codecs/wic.py +11 -2
  129. pyglet/info.py +26 -24
  130. pyglet/input/__init__.py +8 -0
  131. pyglet/input/base.py +163 -105
  132. pyglet/input/controller.py +13 -19
  133. pyglet/input/controller_db.py +39 -24
  134. pyglet/input/emscripten/__init__.py +18 -0
  135. pyglet/input/emscripten/gamepad_js.py +397 -0
  136. pyglet/input/linux/__init__.py +11 -5
  137. pyglet/input/linux/evdev.py +10 -11
  138. pyglet/input/linux/x11_xinput.py +2 -2
  139. pyglet/input/linux/x11_xinput_tablet.py +1 -1
  140. pyglet/input/macos/__init__.py +7 -2
  141. pyglet/input/macos/darwin_gc.py +559 -0
  142. pyglet/input/win32/__init__.py +1 -1
  143. pyglet/input/win32/directinput.py +34 -29
  144. pyglet/input/win32/xinput.py +11 -61
  145. pyglet/lib.py +3 -3
  146. pyglet/libs/__init__.py +1 -1
  147. pyglet/{gl → libs/darwin}/agl.py +1 -1
  148. pyglet/libs/darwin/cocoapy/__init__.py +2 -2
  149. pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
  150. pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
  151. pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
  152. pyglet/libs/darwin/cocoapy/runtime.py +81 -45
  153. pyglet/libs/darwin/coreaudio.py +4 -4
  154. pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
  155. pyglet/libs/darwin/quartzkey.py +1 -3
  156. pyglet/libs/egl/__init__.py +2 -0
  157. pyglet/libs/egl/egl_lib.py +576 -0
  158. pyglet/libs/egl/eglext.py +51 -5
  159. pyglet/libs/linux/__init__.py +0 -0
  160. pyglet/libs/linux/egl/__init__.py +0 -0
  161. pyglet/libs/linux/egl/eglext.py +22 -0
  162. pyglet/libs/linux/glx/__init__.py +0 -0
  163. pyglet/{gl → libs/linux/glx}/glx.py +13 -14
  164. pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
  165. pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
  166. pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
  167. pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
  168. pyglet/libs/linux/wayland/__init__.py +0 -0
  169. pyglet/libs/linux/wayland/client.py +1068 -0
  170. pyglet/libs/linux/wayland/lib_wayland.py +207 -0
  171. pyglet/libs/linux/wayland/wayland_egl.py +38 -0
  172. pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
  173. pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
  174. pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
  175. pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
  176. pyglet/libs/linux/x11/xrandr.py +0 -0
  177. pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
  178. pyglet/libs/shared/__init__.py +0 -0
  179. pyglet/libs/shared/spirv/__init__.py +0 -0
  180. pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
  181. pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
  182. pyglet/libs/win32/__init__.py +27 -5
  183. pyglet/libs/win32/constants.py +59 -48
  184. pyglet/libs/win32/context_managers.py +20 -3
  185. pyglet/libs/win32/dinput.py +105 -88
  186. pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
  187. pyglet/libs/win32/types.py +58 -23
  188. pyglet/{gl → libs/win32}/wgl.py +32 -25
  189. pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
  190. pyglet/media/__init__.py +9 -10
  191. pyglet/media/codecs/__init__.py +12 -1
  192. pyglet/media/codecs/base.py +99 -96
  193. pyglet/media/codecs/ffmpeg.py +2 -2
  194. pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
  195. pyglet/media/codecs/webaudio_pyodide.py +111 -0
  196. pyglet/media/drivers/__init__.py +9 -4
  197. pyglet/media/drivers/base.py +4 -4
  198. pyglet/media/drivers/openal/__init__.py +1 -1
  199. pyglet/media/drivers/openal/adaptation.py +3 -3
  200. pyglet/media/drivers/pulse/__init__.py +1 -1
  201. pyglet/media/drivers/pulse/adaptation.py +3 -3
  202. pyglet/media/drivers/pyodide_js/__init__.py +8 -0
  203. pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
  204. pyglet/media/drivers/xaudio2/adaptation.py +3 -3
  205. pyglet/media/player.py +276 -193
  206. pyglet/media/player_worker_thread.py +1 -1
  207. pyglet/model/__init__.py +39 -29
  208. pyglet/model/codecs/base.py +4 -4
  209. pyglet/model/codecs/gltf.py +3 -3
  210. pyglet/model/codecs/obj.py +71 -43
  211. pyglet/resource.py +129 -78
  212. pyglet/shapes.py +147 -177
  213. pyglet/sprite.py +47 -164
  214. pyglet/text/__init__.py +44 -54
  215. pyglet/text/caret.py +12 -7
  216. pyglet/text/document.py +19 -17
  217. pyglet/text/formats/html.py +2 -2
  218. pyglet/text/formats/structured.py +10 -40
  219. pyglet/text/layout/__init__.py +20 -13
  220. pyglet/text/layout/base.py +176 -287
  221. pyglet/text/layout/incremental.py +9 -10
  222. pyglet/text/layout/scrolling.py +7 -95
  223. pyglet/window/__init__.py +183 -172
  224. pyglet/window/cocoa/__init__.py +62 -51
  225. pyglet/window/cocoa/pyglet_delegate.py +2 -25
  226. pyglet/window/cocoa/pyglet_view.py +9 -8
  227. pyglet/window/dialog/__init__.py +184 -0
  228. pyglet/window/dialog/base.py +99 -0
  229. pyglet/window/dialog/darwin.py +121 -0
  230. pyglet/window/dialog/linux.py +72 -0
  231. pyglet/window/dialog/windows.py +194 -0
  232. pyglet/window/emscripten/__init__.py +779 -0
  233. pyglet/window/headless/__init__.py +44 -28
  234. pyglet/window/key.py +2 -0
  235. pyglet/window/mouse.py +2 -2
  236. pyglet/window/wayland/__init__.py +377 -0
  237. pyglet/window/win32/__init__.py +101 -46
  238. pyglet/window/xlib/__init__.py +104 -66
  239. {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
  240. pyglet-3.0.dev1.dist-info/RECORD +322 -0
  241. {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
  242. pyglet/gl/__init__.py +0 -208
  243. pyglet/gl/base.py +0 -499
  244. pyglet/gl/cocoa.py +0 -309
  245. pyglet/gl/gl.py +0 -4625
  246. pyglet/gl/gl.pyi +0 -2320
  247. pyglet/gl/gl_compat.pyi +0 -3097
  248. pyglet/gl/gl_info.py +0 -190
  249. pyglet/gl/headless.py +0 -166
  250. pyglet/gl/wgl_info.py +0 -36
  251. pyglet/gl/wglext_nv.py +0 -1096
  252. pyglet/gl/win32.py +0 -268
  253. pyglet/gl/xlib.py +0 -295
  254. pyglet/image/buffer.py +0 -274
  255. pyglet/image/codecs/s3tc.py +0 -354
  256. pyglet/libs/x11/xrandr.py +0 -166
  257. pyglet-2.1.12.dist-info/RECORD +0 -234
  258. /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
  259. /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
  260. /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
  261. /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
  262. /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
  263. /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
  264. /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
  265. /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
  266. /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
  267. {pyglet-2.1.12.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
@@ -2,20 +2,20 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Sequence
4
4
 
5
- from pyglet.display.headless import HeadlessCanvas
6
-
5
+ import pyglet
7
6
  # from pyglet.window import key
8
7
  # from pyglet.window import mouse
9
8
  from pyglet.event import EventDispatcher
10
- from pyglet.libs.egl import egl
9
+ from pyglet.libs.egl import (
10
+ eglCreatePbufferSurface,
11
+ EGLint,
12
+ EGL_WIDTH,
13
+ EGL_HEIGHT,
14
+ EGL_NONE,
15
+ eglDestroySurface,
16
+ )
11
17
  from pyglet.window import (
12
18
  BaseWindow,
13
- DefaultMouseCursor, # noqa: F401
14
- ImageMouseCursor, # noqa: F401
15
- MouseCursor, # noqa: F401
16
- MouseCursorException, # noqa: F401
17
- NoSuchDisplayException, # noqa: F401
18
- WindowException, # noqa: F401
19
19
  _PlatformEventHandler,
20
20
  _ViewEventHandler,
21
21
  )
@@ -26,11 +26,13 @@ ViewEventHandler = _ViewEventHandler
26
26
 
27
27
 
28
28
  class HeadlessWindow(BaseWindow):
29
- _egl_display_connection = None
30
- _egl_surface = None
29
+ egl_display_connection = None
30
+ egl_surface = None
31
31
 
32
32
  def _recreate(self, changes: Sequence[str]) -> None:
33
- pass
33
+ if 'fullscreen' in changes:
34
+ self.dispatch_event('_on_internal_resize', self._width, self._height)
35
+ self.dispatch_event('on_expose')
34
36
 
35
37
  def flip(self) -> None:
36
38
  if self.context:
@@ -40,6 +42,10 @@ class HeadlessWindow(BaseWindow):
40
42
  if self.context:
41
43
  self.context.set_current()
42
44
 
45
+ def before_draw(self) -> None:
46
+ if self.context:
47
+ self.context.before_draw()
48
+
43
49
  def set_caption(self, caption: str) -> None:
44
50
  pass
45
51
 
@@ -65,7 +71,12 @@ class HeadlessWindow(BaseWindow):
65
71
  pass
66
72
 
67
73
  def set_visible(self, visible: bool = True) -> None:
68
- pass
74
+ if visible:
75
+ self.dispatch_event('_on_internal_resize', self._width, self._height)
76
+ self.dispatch_event('on_show')
77
+ self.dispatch_event('on_expose')
78
+ else:
79
+ self.dispatch_event('on_hide')
69
80
 
70
81
  def minimize(self) -> None:
71
82
  pass
@@ -76,7 +87,7 @@ class HeadlessWindow(BaseWindow):
76
87
  def set_vsync(self, vsync: bool) -> None:
77
88
  pass
78
89
 
79
- def set_mouse_platform_visible(self, platform_visible: bool | None = None) -> None:
90
+ def set_mouse_cursor_platform_visible(self, platform_visible: bool | None = None) -> None:
80
91
  pass
81
92
 
82
93
  def set_exclusive_mouse(self, exclusive: bool = True) -> None:
@@ -96,20 +107,25 @@ class HeadlessWindow(BaseWindow):
96
107
  pass
97
108
 
98
109
  def _create(self) -> None:
99
- self._egl_display_connection = self.display._display_connection # noqa: SLF001
100
-
101
- if not self._egl_surface:
102
- pbuffer_attribs = (egl.EGL_WIDTH, self._width, egl.EGL_HEIGHT, self._height, egl.EGL_NONE)
103
- pbuffer_attrib_array = (egl.EGLint * len(pbuffer_attribs))(*pbuffer_attribs)
104
- self._egl_surface = egl.eglCreatePbufferSurface(self._egl_display_connection,
105
- self.config._egl_config, # noqa: SLF001
106
- pbuffer_attrib_array)
107
-
108
- self.canvas = HeadlessCanvas(self.display, self._egl_surface)
109
-
110
- self.context.attach(self.canvas)
111
-
112
- self.dispatch_event('_on_internal_resize', self._width, self._height)
110
+ self.egl_display_connection = self.display._display_connection # noqa: SLF001
111
+ if pyglet.options.backend and not self.egl_surface and not self._shadow:
112
+ self._assign_config()
113
+ pbuffer_attribs = (EGL_WIDTH, self._width, EGL_HEIGHT, self._height, EGL_NONE)
114
+ pbuffer_attrib_array = (EGLint * len(pbuffer_attribs))(*pbuffer_attribs)
115
+ self.egl_surface = eglCreatePbufferSurface(self.egl_display_connection,
116
+ self.config._egl_config, # noqa: SLF001
117
+ pbuffer_attrib_array)
118
+
119
+ if not self.egl_surface:
120
+ raise Exception("Failed to create EGL Surface.")
121
+ self.context.attach(self)
122
+
123
+ def close(self) -> None:
124
+ super().close()
125
+ if self.egl_surface:
126
+ eglDestroySurface(self.egl_display_connection, self.egl_surface)
127
+ self.egl_surface = None
128
+ self.egl_display_connection = None
113
129
 
114
130
 
115
131
  __all__ = ['HeadlessWindow']
pyglet/window/key.py CHANGED
@@ -383,6 +383,8 @@ BAR = 0x07c
383
383
  BRACERIGHT = 0x07d
384
384
  ASCIITILDE = 0x07e
385
385
 
386
+
387
+
386
388
  _key_names = {}
387
389
  _motion_names = {}
388
390
  for _name, _value in locals().copy().items():
pyglet/window/mouse.py CHANGED
@@ -47,7 +47,7 @@ class MouseStateHandler:
47
47
  """
48
48
 
49
49
  def __init__(self) -> None: # noqa: D107
50
- self.data = {
50
+ self.data: dict[int | str, int | bool] = {
51
51
  'x': 0,
52
52
  'y': 0,
53
53
  }
@@ -69,7 +69,7 @@ class MouseStateHandler:
69
69
  self.data['x'] = x
70
70
  self.data['y'] = y
71
71
 
72
- def __getitem__(self, key: str) -> int | bool:
72
+ def __getitem__(self, key: str | int) -> int | bool:
73
73
  return self.data.get(key, False)
74
74
 
75
75
  def __getattr__(self, item: str) -> int:
@@ -0,0 +1,377 @@
1
+ from __future__ import annotations
2
+
3
+ import ctypes
4
+ import mmap
5
+ import os
6
+ from ctypes import create_string_buffer
7
+ from typing import Sequence
8
+
9
+ import pyglet
10
+
11
+ from pyglet.event import EventDispatcher
12
+ from pyglet.libs.linux.egl import egl
13
+ from pyglet.libs.linux.wayland import xkbcommon
14
+ from pyglet.window import BaseWindow, _PlatformEventHandler, _ViewEventHandler, key, mouse
15
+ from pyglet.libs.linux.wayland.client import Client, Interface
16
+ from pyglet.libs.linux.wayland.wayland_egl import *
17
+
18
+ # Platform event data is single item, so use platform event handler directly.
19
+ WaylandEventHandler = _PlatformEventHandler
20
+ ViewEventHandler = _ViewEventHandler
21
+
22
+
23
+ # symbol,ctrl -> motion mapping
24
+ _motion_map: dict[tuple[int, bool], int] = {
25
+ (key.UP, False): key.MOTION_UP,
26
+ (key.RIGHT, False): key.MOTION_RIGHT,
27
+ (key.DOWN, False): key.MOTION_DOWN,
28
+ (key.LEFT, False): key.MOTION_LEFT,
29
+ (key.RIGHT, True): key.MOTION_NEXT_WORD,
30
+ (key.LEFT, True): key.MOTION_PREVIOUS_WORD,
31
+ (key.HOME, False): key.MOTION_BEGINNING_OF_LINE,
32
+ (key.END, False): key.MOTION_END_OF_LINE,
33
+ (key.PAGEUP, False): key.MOTION_PREVIOUS_PAGE,
34
+ (key.PAGEDOWN, False): key.MOTION_NEXT_PAGE,
35
+ (key.HOME, True): key.MOTION_BEGINNING_OF_FILE,
36
+ (key.END, True): key.MOTION_END_OF_FILE,
37
+ (key.BACKSPACE, False): key.MOTION_BACKSPACE,
38
+ (key.DELETE, False): key.MOTION_DELETE,
39
+ (key.C, True): key.MOTION_COPY,
40
+ (key.V, True): key.MOTION_PASTE,
41
+ }
42
+
43
+ _modifier_map: dict[int, int] = {
44
+ key.LCTRL: key.MOD_CTRL, key.RCTRL: key.MOD_CTRL,
45
+ key.LSHIFT: key.MOD_SHIFT, key.RSHIFT: key.MOD_SHIFT,
46
+ key.LALT: key.MOD_ALT, key.RALT: key.MOD_ALT,
47
+ key.LWINDOWS: key.MOD_WINDOWS, key.RWINDOWS: key.MOD_WINDOWS,
48
+ key.CAPSLOCK: key.MOD_CAPSLOCK,
49
+ key.NUMLOCK: key.MOD_NUMLOCK,
50
+ key.SCROLLLOCK: key.MOD_SCROLLLOCK,
51
+ }
52
+
53
+
54
+ class WaylandWindow(BaseWindow):
55
+
56
+ egl_display_connection = None
57
+ egl_surface = None
58
+
59
+ _protocols = ['/usr/share/wayland/wayland.xml', '/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml']
60
+ client: Client = None
61
+ wl_compositor: Interface
62
+ wl_surface: Interface
63
+ xdg_wm_base: Interface
64
+ wdg_surface: Interface
65
+ xdg_toplevel: Interface
66
+ wl_pointer: Interface
67
+
68
+ def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
69
+ self._mouse_buttons = 0
70
+ self._key_modifiers = 0
71
+ self._has_keyboard_focus = False
72
+
73
+ self.xkb_context = xkbcommon.xkb_context_new(xkbcommon.enum_xkb_context_flags(xkbcommon.XKB_CONTEXT_NO_FLAGS))
74
+ self.xkb_keymap = None
75
+ self.xkb_state_withmod = None # updated when modifiers are pressed
76
+ self.xkb_state_default = None # never gets updated (for reference)
77
+ self._text_buffer = create_string_buffer(4)
78
+ super().__init__(*args, **kwargs)
79
+
80
+ def _recreate(self, changes: Sequence[str]) -> None:
81
+ pass
82
+
83
+ def flip(self) -> None:
84
+ if self.context:
85
+ self.context.flip()
86
+
87
+ def switch_to(self) -> None:
88
+ if self.context:
89
+ self.context.set_current()
90
+
91
+ def set_caption(self, caption: str) -> None:
92
+ pass
93
+
94
+ def set_minimum_size(self, width: int, height: int) -> None:
95
+ pass
96
+
97
+ def set_maximum_size(self, width: int, height: int) -> None:
98
+ pass
99
+
100
+ def set_size(self, width: int, height: int) -> None:
101
+ pass
102
+
103
+ def get_size(self) -> tuple[int, int]:
104
+ return self._width, self._height
105
+
106
+ def set_location(self, x: int, y: int) -> None:
107
+ pass
108
+
109
+ def get_location(self) -> None:
110
+ pass
111
+
112
+ def activate(self) -> None:
113
+ pass
114
+
115
+ def set_visible(self, visible: bool = True) -> None:
116
+ pass
117
+
118
+ def minimize(self) -> None:
119
+ pass
120
+
121
+ def maximize(self) -> None:
122
+ pass
123
+
124
+ def set_vsync(self, vsync: bool) -> None:
125
+ pass
126
+
127
+ def set_mouse_platform_visible(self, platform_visible: bool | None = None) -> None:
128
+ pass
129
+
130
+ def set_exclusive_mouse(self, exclusive: bool = True) -> None:
131
+ pass
132
+
133
+ def set_exclusive_keyboard(self, exclusive: bool = True) -> None:
134
+ pass
135
+
136
+ def get_system_mouse_cursor(self, name: str) -> None:
137
+ pass
138
+
139
+ def dispatch_events(self) -> None:
140
+ while self._event_queue:
141
+ EventDispatcher.dispatch_event(self, *self._event_queue.pop(0))
142
+
143
+ def dispatch_pending_events(self) -> None:
144
+ pass
145
+
146
+ def before_draw(self) -> None:
147
+ pass
148
+
149
+ def _create(self) -> None:
150
+ self.egl_display_connection = self.display.display_connection
151
+
152
+ self._dpi = self._screen.get_dpi()
153
+ self._scale = self._screen.get_scale() if pyglet.options.dpi_scaling == "stretch" else 1.0
154
+
155
+ # if self._fullscreen:
156
+ # width, height = self.screen.width, self.screen.height
157
+ # else:
158
+ # width, height = self._width, self._height
159
+ # self._view_x = self._view_y = 0
160
+ # if pyglet.options.dpi_scaling in ("scaled", "stretch"):
161
+ # w, h = self.get_requested_size()
162
+ # self._width = width = int(w * self.scale)
163
+ # self._height = height = int(h * self.scale)
164
+ self.client = self.display.client
165
+ self.client.sync()
166
+
167
+ self.wl_compositor = self.client.protocol_dict['wayland'].bind_interface('wl_compositor')
168
+
169
+ self.wl_surface = self.wl_compositor.create_surface(next(self.client.oid_pool))
170
+ self.wl_surface.set_handler('preferred_buffer_scale', self.wl_surface_preferred_buffer_scale_handler)
171
+
172
+ self.xdg_wm_base = self.client.protocol_dict['xdg_shell'].bind_interface('xdg_wm_base')
173
+ self.xdg_wm_base.set_handler('ping', self.xdg_base_ping_handler)
174
+
175
+ self.xdg_surface = self.xdg_wm_base.get_xdg_surface(next(self.client.oid_pool), self.wl_surface)
176
+ self.xdg_surface.set_handler('configure', self.xdg_surface_configure_handler)
177
+
178
+ self.xdg_toplevel = self.xdg_surface.get_toplevel(next(self.client.oid_pool))
179
+ self.xdg_toplevel.set_handlers(
180
+ configure=self.xdg_toplevel_configure_handler,
181
+ configure_bounds=self.xdg_toplevel_configure_bounds,
182
+ close=self.xdg_toplevel_close_handler,
183
+ )
184
+ self.xdg_toplevel.set_parent(None)
185
+ self.xdg_toplevel.set_app_id(self._caption)
186
+
187
+ self.wl_seat = self.client.protocol_dict['wayland'].bind_interface('wl_seat')
188
+ self.wl_pointer = self.wl_seat.get_pointer(next(self.client.oid_pool))
189
+ self.wl_pointer.set_handlers(motion=self.wl_pointer_motion_handler,
190
+ button=self.wl_pointer_button_handler,
191
+ axis_value120=self.wl_pointer_axis_value120_handler,
192
+ enter=self.wl_pointer_enter_handler,
193
+ leave=self.wl_pointer_leave_handler)
194
+ # frame', self.wl_pointer_frame_handler)
195
+
196
+ self.wl_keyboard = self.wl_seat.get_keyboard(next(self.client.oid_pool))
197
+ self.wl_keyboard.set_handlers(
198
+ keymap=self.wl_keyboard_keymap_handler,
199
+ modifiers=self.wl_keyboard_modifiers_handler,
200
+ key=self.wl_keyboard_key_handler,
201
+ enter=self.wl_keyboard_enter_handler,
202
+ leave=self.wl_keyboard_leave_handler,
203
+ )
204
+
205
+ # Used for software render?
206
+ # import os, tempfile
207
+ # fd, name = tempfile.mkstemp()
208
+ # _data_size = self._width * self._height * 4 # width x height x rgba
209
+ # os.write(fd, b'\xee\x33\x33\xee' * self._width * self._height) # BGRA
210
+ #
211
+ # wl_shm = self.client.protocol_dict['wayland'].bind_interface('wl_shm')
212
+ # wl_shm_pool = wl_shm.create_pool(next(self.client.oid_pool), fd, _data_size)
213
+ # wl_buffer = wl_shm_pool.create_buffer(next(self.client.oid_pool), 0, self._width, self._height,
214
+ # self._width * 4, 0)
215
+ # self.wl_surface.attach(wl_buffer.oid, 0, 0)
216
+
217
+ if not self.egl_surface and not self._shadow:
218
+ # An EGL window needs to be created from a Wayland surface,
219
+ egl_window = wl_egl_window_create(self.wl_surface._proxy, self._width, self._height)
220
+
221
+ if not egl_window:
222
+ err = egl.eglGetError()
223
+ msg = f"Failed to create EGL wayland window. Error: 0x{err:04x}"
224
+ raise Exception(msg)
225
+
226
+ self._assign_config()
227
+
228
+ self.egl_surface = egl.eglCreateWindowSurface(
229
+ self.egl_display_connection,
230
+ self.config._egl_config,
231
+ egl_window,
232
+ None,
233
+ )
234
+
235
+ if not self.egl_surface:
236
+ err = egl.eglGetError()
237
+ msg = f"Failed to create egl surface. Error: 0x{err:04x}"
238
+ raise Exception(msg)
239
+
240
+ # self.canvas = WaylandCanvas(self.display, self.egl_surface)
241
+ self.context.attach(self)
242
+ self.dispatch_event('_on_internal_resize', self._width, self._height)
243
+
244
+ # Start Wayland event handlers
245
+
246
+ def xdg_base_ping_handler(self, serial):
247
+ """Keep-alive response to the ping event"""
248
+ self.xdg_wm_base.pong(serial)
249
+
250
+ def wl_surface_preferred_buffer_scale_handler(self, factor):
251
+ print(f" --> wl_surface scaling: {factor}")
252
+
253
+ def xdg_toplevel_configure_handler(self, width, height, states):
254
+ print(" --> xdg_toplevel configure event", width, height, states)
255
+
256
+ def xdg_toplevel_configure_bounds(self, width, height):
257
+ print(" --> xdg_toplevel_configure_bounds event", width, height)
258
+
259
+ def xdg_toplevel_close_handler(self):
260
+ self.dispatch_event('on_close')
261
+
262
+ def xdg_surface_configure_handler(self, *args):
263
+ print(" --> xdg_surface configure event", args)
264
+ self.xdg_surface.ack_configure(args[0])
265
+
266
+ def wl_pointer_button_handler(self, serial, time, button, state):
267
+ mouse_button = {0x110: mouse.LEFT,
268
+ 0x111: mouse.RIGHT,
269
+ 0x112: mouse.MIDDLE,
270
+ 0x113: mouse.MOUSE4,
271
+ 0x114: mouse.MOUSE5}[button]
272
+
273
+ if self.wl_pointer.enums['button_state'][state].name == 'pressed':
274
+ self._mouse_buttons |= mouse_button
275
+ self.dispatch_event('on_mouse_press', self._mouse_x, self._mouse_y, mouse_button, self._key_modifiers)
276
+
277
+ elif self.wl_pointer.enums['button_state'][state].name == 'released':
278
+ self._mouse_buttons &= ~mouse_button
279
+ self.dispatch_event('on_mouse_release', self._mouse_x, self._mouse_y, mouse_button, self._key_modifiers)
280
+
281
+ def wl_pointer_motion_handler(self, time, surface_x, surface_y):
282
+ x = surface_x / self._scale
283
+ # TODO: isolate flipped-Y to a single value
284
+ y = self.height - surface_y / self._scale
285
+ dx = x - self._mouse_x
286
+ dy = y - self._mouse_y
287
+ self._mouse_x = x
288
+ self._mouse_y = y
289
+
290
+ if self._mouse_buttons:
291
+ self.dispatch_event('on_mouse_drag', x, y, dx, dy, self._mouse_buttons, self._key_modifiers)
292
+ else:
293
+ self.dispatch_event('on_mouse_motion', x, y, dx, dy)
294
+
295
+ def wl_pointer_axis_value120_handler(self, axis, value120):
296
+ # TODO: combine discreet events & dispatch with wl_pointer frame event.
297
+ # 0: vertical axis, 1: horizontal axis
298
+ axis = self.wl_pointer.enums['axis'][axis].value
299
+ scroll_x, scroll_y = {0: (0, -1), 1: (-1, 0)}[axis]
300
+ scroll_x *= (value120 / 120)
301
+ scroll_y *= (value120 / 120)
302
+ self.dispatch_event('on_mouse_scroll', self._mouse_x, self._mouse_y, scroll_x, scroll_y)
303
+
304
+ # def wl_pointer_frame_handler(self):
305
+ # print("end of pointer frame")
306
+
307
+ def wl_pointer_enter_handler(self, serial, surface, surface_x, surface_y):
308
+ # TODO: make sure it's the main app surface
309
+ self._mouse_in_window = True
310
+ self.dispatch_event('on_mouse_enter', surface_x, surface_y)
311
+
312
+ def wl_pointer_leave_handler(self, serial, surface):
313
+ # TODO: make sure it's the main app surface
314
+ self._mouse_in_window = False
315
+ self.dispatch_event('on_mouse_leave', self._mouse_x, self._mouse_y)
316
+
317
+ def wl_keyboard_enter_handler(self, serial, surface, keys):
318
+ # TODO: process held keys?
319
+ self._has_keyboard_focus = True
320
+
321
+ def wl_keyboard_leave_handler(self, serial, surface):
322
+ self._has_keyboard_focus = False
323
+
324
+ def wl_keyboard_keymap_handler(self, fmt, fd, size):
325
+ # Does not work reliably without mapping:
326
+ mmap_obj = mmap.mmap(fd, size, prot=mmap.PROT_READ, flags=mmap.MAP_PRIVATE)
327
+ data = ctypes.create_string_buffer(mmap_obj.read())
328
+ os.close(fd)
329
+
330
+ # Note: There is only one format, so no need to check:
331
+ # fmt = self.wl_keyboard.enums['keymap_format'][fmt].name
332
+ fmt = xkbcommon.enum_xkb_keymap_format(xkbcommon.XKB_KEYMAP_FORMAT_TEXT_V1)
333
+ flags = xkbcommon.enum_xkb_keymap_compile_flags(xkbcommon.XKB_KEYMAP_COMPILE_NO_FLAGS)
334
+ if xkb_keymap := xkbcommon.xkb_keymap_new_from_string(self.xkb_context, data, fmt, flags):
335
+ self.xkb_keymap = xkb_keymap
336
+ self.xkb_state_withmod = xkbcommon.xkb_state_new(self.xkb_keymap)
337
+ self.xkb_state_default = xkbcommon.xkb_state_new(self.xkb_keymap)
338
+
339
+ def wl_keyboard_modifiers_handler(self, serial, mods_depressed, mods_latched, mods_locked, group):
340
+ xkbcommon.xkb_state_update_mask(
341
+ self.xkb_state_withmod, mods_depressed, mods_latched, mods_locked, group, group, group)
342
+
343
+ def wl_keyboard_key_handler(self, serial, time, keycode, state):
344
+ keycode += 8 # xkbcommon expects +8
345
+ symbol = xkbcommon.xkb_state_key_get_one_sym(self.xkb_state_default, keycode)
346
+ modifier = _modifier_map.get(symbol, 0)
347
+
348
+ # released 0, pressed 1, repeated 2
349
+ state_name = self.wl_keyboard.enums['key_state'][state].name
350
+
351
+ if state_name == 'pressed':
352
+ _size = xkbcommon.xkb_state_key_get_utf8(self.xkb_state_withmod, keycode, self._text_buffer, 4)
353
+ character = self._text_buffer[:_size].decode()
354
+
355
+ self.dispatch_event('on_text', character)
356
+ self.dispatch_event('on_key_press', symbol, self._key_modifiers)
357
+ self._key_modifiers |= modifier
358
+
359
+ if self._key_modifiers & key.MOD_ALT:
360
+ return
361
+
362
+ ctrl = self._key_modifiers & key.MOD_CTRL != 0
363
+ shft = self._key_modifiers & key.MOD_SHIFT != 0
364
+ if motion := _motion_map.get((symbol, ctrl), None):
365
+ if shft:
366
+ self.dispatch_event('on_text_motion_select', motion)
367
+ else:
368
+ self.dispatch_event('on_text_motion', motion)
369
+
370
+ if state_name == 'released':
371
+ self.dispatch_event('on_key_release', symbol, self._key_modifiers)
372
+ self._key_modifiers &= ~modifier
373
+
374
+ # End Wayland event handlers
375
+
376
+
377
+ __all__ = ['WaylandWindow']