pyglet 2.1.13__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 +5 -21
  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 +28 -8
  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 +154 -194
  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.13.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.13.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.13.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.13.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
@@ -4,7 +4,6 @@ from ctypes import c_void_p
4
4
  from typing import TYPE_CHECKING, Sequence
5
5
 
6
6
  import pyglet
7
- from pyglet.display.cocoa import CocoaCanvas
8
7
  from pyglet.event import EventDispatcher
9
8
  from pyglet.libs.darwin import AutoReleasePool, CGPoint, cocoapy
10
9
  from pyglet.window import BaseWindow, DefaultMouseCursor, MouseCursor
@@ -17,7 +16,7 @@ from .pyglet_window import PygletToolWindow, PygletWindow
17
16
  from .systemcursor import SystemCursor
18
17
 
19
18
  if TYPE_CHECKING:
20
- from pyglet.gl.cocoa import CocoaContext
19
+ from pyglet.graphics.api.gl.cocoa.context import CocoaContext
21
20
 
22
21
  NSApplication = cocoapy.ObjCClass('NSApplication')
23
22
  NSCursor = cocoapy.ObjCClass('NSCursor')
@@ -30,9 +29,11 @@ NSPasteboard = cocoapy.ObjCClass('NSPasteboard')
30
29
  quartz = cocoapy.quartz
31
30
  cf = cocoapy.cf
32
31
 
32
+ CAMetalLayer = cocoapy.ObjCClass('CAMetalLayer')
33
+
33
34
 
34
35
  class CocoaMouseCursor(MouseCursor):
35
- gl_drawable = False
36
+ api_drawable = False
36
37
 
37
38
  def __init__(self, cursorName: str) -> None:
38
39
  # cursorName is a string identifying one of the named default NSCursors
@@ -93,26 +94,20 @@ class CocoaWindow(BaseWindow):
93
94
  def _create(self) -> None:
94
95
  with AutoReleasePool():
95
96
  if self._nswindow:
96
- # The window is about the be recreated so destroy everything
97
+ # The window is about to be recreated so destroy everything
97
98
  # associated with the old window, then destroy the window itself.
98
- nsview = self.canvas.nsview
99
- self.canvas = None
100
99
  self._nswindow.orderOut_(None)
101
100
  self._nswindow.close()
102
101
  self.context.detach()
103
102
  self._nswindow.release()
104
103
  self._nswindow = None
105
- nsview.release()
104
+ self._nsview.release()
105
+ self._nsview = None
106
106
  self._delegate.release()
107
107
  self._delegate = None
108
108
 
109
109
  # Determine window parameters.
110
- if pyglet.options.dpi_scaling == "real":
111
- screen_scale = self.screen.get_scale()
112
- w, h = self.get_requested_size()
113
- width, height = w / screen_scale, h / screen_scale
114
- else:
115
- width, height = self._width, self._height
110
+ width, height = self._width, self._height
116
111
 
117
112
  content_rect = cocoapy.NSMakeRect(0, 0, width, height)
118
113
  WindowClass = PygletWindow
@@ -165,25 +160,29 @@ class CocoaWindow(BaseWindow):
165
160
 
166
161
  # Then create a view and set it as our NSWindow's content view.
167
162
  self._nsview = PygletView.alloc().initWithFrame_cocoaWindow_(content_rect, self)
168
- self._nsview.setWantsBestResolutionOpenGLSurface_(True)
169
-
170
- if not self._fullscreen:
171
- if self._style in ("transparent", "overlay"):
172
- self._nswindow.setOpaque_(False)
173
- self._nswindow.setBackgroundColor_(NSColor.clearColor())
174
- self._nswindow.setHasShadow_(False)
175
-
176
- if self._style == "overlay":
177
- self.set_mouse_passthrough(True)
178
- self._nswindow.setLevel_(cocoapy.NSStatusWindowLevel)
179
-
163
+ self._metal_layer = None
164
+ if not self._shadow:
165
+ if "gl" in pyglet.options.backend:
166
+ self._nsview.setWantsBestResolutionOpenGLSurface_(True)
167
+ elif pyglet.options.backend == "vulkan":
168
+ self._metal_layer = CAMetalLayer.alloc().init()
169
+ #self._metal_layer.setFramebufferOnly_(True) # Layer can only be used as a FB. More performant?
170
+ transparent = self.style == 'transparent' or self.style == 'overlay'
171
+ self._metal_layer.setOpaque_(not transparent)
172
+
173
+ # Attach the CAMetalLayer to the NSView
174
+ self._nsview.setLayer_(self._metal_layer)
175
+ self._nsview.setWantsLayer_(True)
176
+ else:
177
+ print(f"Unsupported backend found. '{pyglet.options.backend}'")
178
+
179
+ self._assign_config()
180
+ self.context.attach(self)
181
+ if self._metal_layer:
182
+ self.context._nscontext = self._metal_layer # noqa: SLF001
180
183
  self._nswindow.setContentView_(self._nsview)
181
184
  self._nswindow.makeFirstResponder_(self._nsview)
182
185
 
183
- # Create a canvas with the view as its drawable and attach context to it.
184
- self.canvas = CocoaCanvas(self.display, self.screen, self._nsview)
185
- self.context.attach(self.canvas)
186
-
187
186
  # Configure the window.
188
187
  self._nswindow.setAcceptsMouseMovedEvents_(True)
189
188
 
@@ -208,13 +207,29 @@ class CocoaWindow(BaseWindow):
208
207
  array = NSArray.arrayWithObject_(cocoapy.NSPasteboardTypeURL)
209
208
  self._nsview.registerForDraggedTypes_(array)
210
209
 
211
- self.context.update_geometry()
210
+ self._update_geometry()
212
211
  self.switch_to()
213
212
  self.set_vsync(self._vsync)
214
213
  self.set_visible(self._visible)
215
214
 
215
+ if not self._fullscreen:
216
+ if self._style in ("transparent", "overlay"):
217
+ self._nswindow.setOpaque_(False)
218
+ self._nswindow.setBackgroundColor_(NSColor.clearColor())
219
+ self._nswindow.setHasShadow_(False)
220
+
221
+ if self._style == "overlay":
222
+ self.set_mouse_passthrough(True)
223
+ self._nswindow.setLevel_(cocoapy.NSStatusWindowLevel)
224
+
225
+ def _update_geometry(self):
226
+ if self._metal_layer:
227
+ pass
228
+ else:
229
+ self.context.update_geometry()
230
+
216
231
  def _get_dpi_desc(self) -> int:
217
- if pyglet.options.dpi_scaling in ("scaled", "stretch", "platform") and self._nswindow:
232
+ if self._nswindow:
218
233
  desc = self._nswindow.deviceDescription()
219
234
  rsize = desc.objectForKey_(darwin.NSDeviceResolution).sizeValue()
220
235
  return int(rsize.width)
@@ -227,7 +242,7 @@ class CocoaWindow(BaseWindow):
227
242
 
228
243
  Read only.
229
244
  """
230
- if pyglet.options.dpi_scaling in ("scaled", "stretch", "platform") and self._nswindow:
245
+ if self._nswindow:
231
246
  return self._nswindow.backingScaleFactor()
232
247
 
233
248
  return 1.0
@@ -271,7 +286,7 @@ class CocoaWindow(BaseWindow):
271
286
 
272
287
  with AutoReleasePool():
273
288
  # Restore cursor visibility
274
- self.set_mouse_platform_visible(True)
289
+ self.set_mouse_cursor_platform_visible(True)
275
290
  self.set_exclusive_mouse(False)
276
291
  self.set_exclusive_keyboard(False)
277
292
 
@@ -288,11 +303,7 @@ class CocoaWindow(BaseWindow):
288
303
  self._delegate.release()
289
304
  self._delegate = None
290
305
 
291
- # Remove view from canvas and then remove canvas.
292
- if self.canvas:
293
- self.canvas.nsview = None
294
- self.canvas = None
295
-
306
+ # Remove view.
296
307
  if self._nsview:
297
308
  self._nswindow.setContentView_(None)
298
309
  self._nsview.release()
@@ -314,6 +325,10 @@ class CocoaWindow(BaseWindow):
314
325
  if self.context:
315
326
  self.context.set_current()
316
327
 
328
+ def before_draw(self) -> None:
329
+ if self.context:
330
+ self.context.before_draw()
331
+
317
332
  def flip(self) -> None:
318
333
  self.draw_mouse_cursor()
319
334
  if self.context:
@@ -381,7 +396,7 @@ class CocoaWindow(BaseWindow):
381
396
  image = max_image.get_image_data()
382
397
  fmt = 'ARGB'
383
398
  bytesPerRow = len(fmt) * image.width
384
- data = image.get_data(fmt, -bytesPerRow)
399
+ data = image.get_bytes(fmt, -bytesPerRow)
385
400
 
386
401
  # Use image data to create a data provider.
387
402
  # Using CGDataProviderCreateWithData crashes PyObjC 2.2b3, so we create
@@ -444,7 +459,7 @@ class CocoaWindow(BaseWindow):
444
459
  return self._width, self._height
445
460
 
446
461
  def get_framebuffer_size(self) -> tuple[int, int]:
447
- view = self.context._nscontext.view()
462
+ view = self._nsview
448
463
  bounds = view.bounds()
449
464
  bounds = view.convertRectToBacking_(bounds)
450
465
  return int(bounds.size.width), int(bounds.size.height)
@@ -452,11 +467,7 @@ class CocoaWindow(BaseWindow):
452
467
  def set_size(self, width: int, height: int) -> None:
453
468
  super().set_size(width, height)
454
469
 
455
- if pyglet.options.dpi_scaling == "real":
456
- screen_scale = self._nswindow.backingScaleFactor()
457
- frame_width, frame_height = width // screen_scale, height // screen_scale
458
- else:
459
- frame_width, frame_height = width, height
470
+ frame_width, frame_height = width, height
460
471
 
461
472
  self._set_frame_size(frame_width, frame_height)
462
473
  self.dispatch_event('_on_internal_resize', width, height)
@@ -516,8 +527,8 @@ class CocoaWindow(BaseWindow):
516
527
  self._nswindow.zoom_(None)
517
528
 
518
529
  def set_vsync(self, vsync: bool) -> None:
519
- if pyglet.options['vsync'] is not None:
520
- vsync = pyglet.options['vsync']
530
+ if pyglet.options.vsync is not None:
531
+ vsync = pyglet.options.vsync
521
532
 
522
533
  super().set_vsync(vsync)
523
534
  self.context.set_vsync(vsync)
@@ -531,7 +542,7 @@ class CocoaWindow(BaseWindow):
531
542
  rect = self._nswindow.contentRectForFrameRect_(window_frame)
532
543
  return cocoapy.foundation.NSMouseInRect(point, rect, False)
533
544
 
534
- def set_mouse_platform_visible(self, platform_visible: int | None = None) -> None:
545
+ def set_mouse_cursor_platform_visible(self, platform_visible: int | None = None) -> None:
535
546
  # When the platform_visible argument is supplied with a boolean, then this
536
547
  # method simply sets whether or not the platform mouse cursor is visible.
537
548
  if platform_visible is not None:
@@ -555,7 +566,7 @@ class CocoaWindow(BaseWindow):
555
566
  # If we are in the window, then what we do depends on both
556
567
  # the current pyglet-set visibility setting for the mouse and
557
568
  # the type of the mouse cursor. If the cursor has been hidden
558
- # in the window with set_mouse_visible() then don't show it.
569
+ # in the window with set_mouse_cursor_visible() then don't show it.
559
570
  elif not self._mouse_visible:
560
571
  SystemCursor.hide()
561
572
  # If the mouse is set as a system-defined cursor, then we
@@ -566,7 +577,7 @@ class CocoaWindow(BaseWindow):
566
577
  SystemCursor.unhide()
567
578
  # If the mouse cursor is OpenGL drawable, then it we need to hide
568
579
  # the system mouse cursor, so that the cursor can draw itself.
569
- elif self._mouse_cursor.gl_drawable:
580
+ elif self._mouse_cursor.api_drawable:
570
581
  SystemCursor.hide()
571
582
  # Otherwise, show the default cursor.
572
583
  else:
@@ -639,7 +650,7 @@ class CocoaWindow(BaseWindow):
639
650
  quartz.CGAssociateMouseAndMouseCursorPosition(True)
640
651
 
641
652
  # Update visibility of mouse cursor.
642
- self.set_mouse_platform_visible()
653
+ self.set_mouse_cursor_platform_visible()
643
654
 
644
655
  def set_exclusive_keyboard(self, exclusive: bool = True) -> None:
645
656
  # http://developer.apple.com/mac/library/technotes/tn2002/tn2062.html
@@ -3,11 +3,9 @@ from __future__ import annotations
3
3
  from ctypes import c_void_p
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- import pyglet
7
6
  from pyglet.libs.darwin.cocoapy import (
8
7
  NSApplicationDidHideNotification,
9
8
  NSApplicationDidUnhideNotification,
10
- NSMakeRect,
11
9
  ObjCClass,
12
10
  ObjCInstance,
13
11
  ObjCSubclass,
@@ -101,7 +99,7 @@ class PygletDelegate_Implementation:
101
99
  self.did_pause_exclusive_mouse = False
102
100
  self._window._nswindow.setMovable_(True) # Mac OS 10.6 # noqa: SLF001
103
101
  # Restore previous mouse visibility settings.
104
- self._window.set_mouse_platform_visible()
102
+ self._window.set_mouse_cursor_platform_visible()
105
103
  self._window.dispatch_event('on_activate')
106
104
 
107
105
  @PygletDelegate.method('v@')
@@ -115,7 +113,7 @@ class PygletDelegate_Implementation:
115
113
  # the window is reactivated by clicking on its title bar.
116
114
  self._window._nswindow.setMovable_(False) # Mac OS X 10.6 # noqa: SLF001
117
115
  # Make sure that cursor is visible.
118
- self._window.set_mouse_platform_visible(True)
116
+ self._window.set_mouse_cursor_platform_visible(True)
119
117
  self._window.dispatch_event('on_deactivate')
120
118
 
121
119
  @PygletDelegate.method('v@')
@@ -157,27 +155,6 @@ class PygletDelegate_Implementation:
157
155
  new_scale = self._window._nswindow.backingScaleFactor()
158
156
  if old_scale != new_scale:
159
157
  self._window.switch_to()
160
-
161
- currentFrame = self._window._nswindow.frame()
162
-
163
- if pyglet.options.dpi_scaling == "real":
164
- screen_scale = new_scale
165
- w, h = self._window.get_requested_size()
166
- width, height = int(w / screen_scale), int(h / screen_scale)
167
-
168
- # Force Window back to correct size.
169
- self._window._set_frame_size(width, height)
170
- else:
171
- # MacOS seems to cache the state of the window size, even between different DPI scales/monitors.
172
- # This means that the screen will refuse to refresh until we resize the window to a different size.
173
- # Force a refresh by setting a temporary frame, then forcing it back.
174
- if pyglet.options.dpi_scaling == "scaled":
175
- tempRect = NSMakeRect(currentFrame.origin.x, currentFrame.origin.y,
176
- currentFrame.size.width + 1, currentFrame.size.height + 1)
177
- # TODO: Add variable to ignore the next two on-resize events?
178
- self._window._nswindow.setFrame_display_(tempRect, True)
179
- self._window._nswindow.setFrame_display_(currentFrame, True)
180
-
181
158
  self._window.context.update_geometry()
182
159
  self._window.dispatch_event("_on_internal_scale", new_scale, self._window._get_dpi_desc())
183
160
 
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import ctypes
4
3
  from typing import TYPE_CHECKING
5
4
 
6
5
  import pyglet
@@ -11,12 +10,13 @@ from pyglet.libs.darwin import (
11
10
  NSLeftCommandKeyMask,
12
11
  NSLeftControlKeyMask,
13
12
  NSLeftShiftKeyMask,
13
+ NSMakeRect,
14
14
  NSPasteboardURLReadingFileURLsOnlyKey,
15
15
  NSRightAlternateKeyMask,
16
16
  NSRightCommandKeyMask,
17
17
  NSRightControlKeyMask,
18
18
  NSRightShiftKeyMask,
19
- cocoapy, NSMakeRect,
19
+ cocoapy,
20
20
  )
21
21
  from pyglet.libs.darwin.quartzkey import charmap, keymap
22
22
  from pyglet.window import key, mouse
@@ -24,6 +24,7 @@ from pyglet.window import key, mouse
24
24
  from .pyglet_textview import PygletTextView
25
25
 
26
26
  if TYPE_CHECKING:
27
+ import ctypes
27
28
  from . import CocoaWindow
28
29
 
29
30
  NSTrackingArea = cocoapy.ObjCClass('NSTrackingArea')
@@ -193,18 +194,18 @@ class PygletView_Implementation:
193
194
  # This method is called when view is first installed as the
194
195
  # contentView of window. Don't do anything on first call.
195
196
  # This also helps ensure correct window creation event ordering.
196
- if not self._window.context.canvas or self._window._shadow:
197
+ if not self._window.context.window or self._window._shadow: # noqa: SLF001
197
198
  return
198
199
 
199
200
  width, height = int(size.width), int(size.height)
200
201
  self._window.switch_to()
201
- self._window.context.update_geometry()
202
+ self._window._update_geometry()
202
203
  self._window._width, self._window._height = width, height # noqa: SLF001
203
204
  self._window.dispatch_event('_on_internal_resize', width, height)
204
205
  self._window.dispatch_event('on_expose')
205
206
  # Can't get app.event_loop.enter_blocking() working with Cocoa, because
206
207
  # when mouse clicks on the window's resize control, Cocoa enters into a
207
- # mini-event loop that only responds to mouseDragged and mouseUp events.
208
+ # mini-event loop that only responds to mouseDragged and mouseUp events.
208
209
  # This means that using NSTimer to call idle() won't work. Our kludge
209
210
  # is to override NSWindow's nextEventMatchingMask_etc method and call
210
211
  # idle() from there.
@@ -340,7 +341,7 @@ class PygletView_Implementation:
340
341
  def mouseEntered_(self, nsevent: cocoapy.ObjCInstance) -> None:
341
342
  x, y = getMousePosition(self, nsevent)
342
343
  self._window._mouse_in_window = True # noqa: SLF001
343
- # Don't call self._window.set_mouse_platform_visible() from here.
344
+ # Don't call self._window.set_mouse_cursor_platform_visible() from here.
344
345
  # Better to do it from cursorUpdate:
345
346
  self._window.dispatch_event('on_mouse_enter', x, y)
346
347
 
@@ -349,7 +350,7 @@ class PygletView_Implementation:
349
350
  x, y = getMousePosition(self, nsevent)
350
351
  self._window._mouse_in_window = False # noqa: SLF001
351
352
  if not self._window._mouse_exclusive: # noqa: SLF001
352
- self._window.set_mouse_platform_visible()
353
+ self._window.set_mouse_cursor_platform_visible()
353
354
  self._window.dispatch_event('on_mouse_leave', x, y)
354
355
 
355
356
  @PygletView.method('v@')
@@ -363,7 +364,7 @@ class PygletView_Implementation:
363
364
  # to the default arrow and screw up our cursor tracking.
364
365
  self._window._mouse_in_window = True # noqa: SLF001
365
366
  if not self._window._mouse_exclusive: # noqa: SLF001
366
- self._window.set_mouse_platform_visible()
367
+ self._window.set_mouse_cursor_platform_visible()
367
368
 
368
369
  @PygletView.method('Q@')
369
370
  def draggingEntered_(self, draginfo: cocoapy.ObjCInstance) -> int:
@@ -0,0 +1,184 @@
1
+ from __future__ import annotations
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import pyglet
6
+ from pyglet.event import EventDispatcher as _EventDispatcher, EVENT_HANDLE_STATE
7
+
8
+ """File dialog classes for opening and saving files.
9
+
10
+ This module provides example Dialog Windows for opening and
11
+ saving files. These are made using the `tkinter` module, which
12
+ is part of the Python standard library. Dialog Windows run in a
13
+ background process to prevent any interference with your main
14
+ application, and integrate using the standard pyglet Event
15
+ framework.
16
+
17
+ Note that these dialogs do not actually open or save any data
18
+ to disk. They simply return one or more strings, which contain
19
+ the final file paths that were selected or entered. You can
20
+ then use this information in your main application to handle
21
+ the disk IO. This is done by attaching an event handler to the
22
+ dialog, which will receive the file path(s) as an argument.
23
+
24
+ Create a `FileOpenDialog` instance, and attach a handler to it::
25
+
26
+ # Restrict to only showing ".png" and ".bmp" file types,
27
+ # and allow selecting more than one file
28
+ open_dialog = FileOpenDialog(filetypes=[("PNG", ".png"), ("24-bit Bitmap", ".bmp")], multiple=True)
29
+
30
+ # Multiple filetypes can be specified in the same string as long as a space is used. Wildcards are also accepted.
31
+ open_dialog = FileOpenDialog(filetypes=[("Images", "*.png *.bmp *.jpg")], multiple=True)
32
+
33
+ @open_dialog.event
34
+ def on_dialog_open(filenames):
35
+ print("list of selected filenames:", filenames)
36
+ # Your own code here to handle loading the file name(s).
37
+
38
+ # Show the Dialog whenever you need. This is non-blocking:
39
+ open_dialog.show()
40
+
41
+
42
+ The `FileSaveDialog` works similarly::
43
+
44
+ # Add a default file extension ".sav" to the file
45
+ save_as = FileSaveDialog(default_ext='.sav')
46
+
47
+ @save_as.event
48
+ def on_dialog_save(filename):
49
+ print("FILENAMES ON SAVE!", filename)
50
+ # Your own code here to handle saving the file name(s).
51
+
52
+ # Show the Dialog whenever you need. This is non-blocking:
53
+ open_dialog.show()
54
+ """
55
+
56
+ _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
57
+
58
+ class _OpenDialogMeta(type):
59
+ def __new__(cls, name: str, bases: tuple, attrs: dict) -> type:
60
+ if _is_pyglet_doc_run:
61
+ return super().__new__(cls, name, bases, attrs)
62
+
63
+ if pyglet.compat_platform == "win32":
64
+ from pyglet.window.dialog.windows import WindowsFileOpenDialog # noqa: PLC0415
65
+ return WindowsFileOpenDialog
66
+ if pyglet.compat_platform == "darwin":
67
+ from pyglet.window.dialog.darwin import MacOSFileOpenDialog # noqa: PLC0415
68
+ return MacOSFileOpenDialog
69
+ if pyglet.compat_platform == "linux":
70
+ from pyglet.window.dialog.linux import TkFileOpenDialog # noqa: PLC0415
71
+ return TkFileOpenDialog
72
+
73
+ return super().__new__(cls, name, bases, attrs)
74
+
75
+
76
+ class FileOpenDialog(_EventDispatcher, metaclass=_OpenDialogMeta):
77
+ """Opens a system file dialog window for opening files.
78
+
79
+ .. versionadded:: 3.0
80
+ """
81
+ def __init__(
82
+ self, title: str="Open File", initial_dir: str | Path | None= None, initial_file: str | None=None,
83
+ filetypes: list[tuple[str, str]] | None=None, multiple: bool=False) -> None:
84
+ """Establish how the open file dialog will behave.
85
+
86
+ Args:
87
+ title:
88
+ The Dialog Window name. Defaults to "Open File".
89
+ initial_dir:
90
+ The directory to start in. If a path is not given, it is up the OS to determine behavior.
91
+ On Windows, if None is passed, it will open to the last used directory.
92
+ initial_file:
93
+ The filename to prepopulate with when opening. Not supported on Mac OS.
94
+ filetypes:
95
+ An optional list of tuples containing (name, extension) to filter by.
96
+ If none are given, all files will be shown and selectable.
97
+ For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
98
+ For multiple file types in the same selection, separate by a semicolon.
99
+ For example: [("Images", ".png;.bmp")]`
100
+ multiple: bool
101
+ True if multiple files can be selected. Defaults to False.
102
+ """
103
+ self.title = title
104
+ self.initial_dir = initial_dir
105
+ self.filetypes = filetypes
106
+ self.multiple = multiple
107
+ self.initial_file = initial_file
108
+
109
+ def open(self) -> None:
110
+ """Open a file dialog window to select files according to the configuration."""
111
+ raise NotImplementedError
112
+
113
+ def on_dialog_open(self, filenames: list[str]) -> EVENT_HANDLE_STATE:
114
+ """Event for filename choice.
115
+
116
+ Args:
117
+ filenames:
118
+ The selected filename paths chosen by the user. May be empty if nothing is chosen.
119
+ """
120
+
121
+ class _SaveDialogMeta(type):
122
+ def __new__(cls, name: str, bases: tuple, attrs: dict) -> type:
123
+ if _is_pyglet_doc_run:
124
+ return super().__new__(cls, name, bases, attrs)
125
+
126
+ if pyglet.compat_platform == "win32":
127
+ from pyglet.window.dialog.windows import WindowsFileSaveDialog # noqa: PLC0415
128
+ return WindowsFileSaveDialog
129
+ if pyglet.compat_platform == "darwin":
130
+ from pyglet.window.dialog.darwin import MacOSFileSaveDialog # noqa: PLC0415
131
+ return MacOSFileSaveDialog
132
+ if pyglet.compat_platform == "linux":
133
+ from pyglet.window.dialog.linux import TkFileSaveDialog # noqa: PLC0415
134
+ return TkFileSaveDialog
135
+
136
+ return super().__new__(cls, name, bases, attrs)
137
+
138
+ class FileSaveDialog(_EventDispatcher, metaclass=_SaveDialogMeta):
139
+ """Opens a system file dialog window for saving files.
140
+
141
+ .. versionadded:: 3.0
142
+ """
143
+ def __init__(self, title: str="Save As", initial_dir: str | Path | None=None, initial_file: str | None=None,
144
+ filetypes: list[tuple[str, str]] | None=None, default_ext: str="") -> None:
145
+ """Establish how the save file dialog will behave.
146
+
147
+ Args:
148
+ title:
149
+ The Dialog Window name. Defaults to "Save As".
150
+ initial_dir:
151
+ The directory to start in. If a path is not given, it is up the OS to determine behavior.
152
+ On Windows, if None is passed, it will open to the last used directory.
153
+ initial_file:
154
+ A default file name to be filled in. Defaults to None.
155
+ filetypes:
156
+ An optional list of tuples containing (name, extension) to
157
+ filter to. If the `default_ext` argument is not given, this list
158
+ also dictates the extension that will be added to the entered
159
+ file name. If a list of `filetypes` are not give, you can enter
160
+ any file name to save as.
161
+ For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
162
+ default_ext:
163
+ A default file extension to add to the file. This will override
164
+ the `filetypes` list if given, but will not override a manually
165
+ entered extension.
166
+ """
167
+ self.title = title
168
+ self.initial_dir = initial_dir or Path.cwd()
169
+ self.filetypes = filetypes
170
+ self.initial_file = initial_file
171
+ self.default_ext = default_ext
172
+
173
+ def open(self) -> None:
174
+ """Open the file dialog window to save files according to the configuration."""
175
+ raise NotImplementedError
176
+
177
+ def on_dialog_save(self, filename: str) -> EVENT_HANDLE_STATE:
178
+ """Event for filename choice.
179
+
180
+ Args:
181
+ filename:
182
+ The resulting filename a user input. The string may be empty if user input nothing or cancelled
183
+ the operation.
184
+ """
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+
5
+ from pyglet.event import EventDispatcher as _EventDispatcher
6
+ from pathlib import Path
7
+
8
+
9
+ class _Dialog(_EventDispatcher):
10
+ """Dialog base class
11
+
12
+ This base class sets up a ProcessPoolExecutor with a single
13
+ background Process. This allows the Dialog to display in
14
+ the background without blocking or interfering with the main
15
+ application Process. This also limits to a single open Dialog
16
+ at a time.
17
+ """
18
+ _dialog = None
19
+
20
+ @abstractmethod
21
+ def open(self):
22
+ ...
23
+
24
+ def _dispatch_event(self, future):
25
+ raise NotImplementedError
26
+
27
+
28
+ class FileOpenDialogBase(_Dialog):
29
+ def __init__(
30
+ self, title: str="Open File", initial_dir: str | Path | None = None, initial_file: str | None=None,
31
+ filetypes: list[tuple[str, str]] | None=None, multiple: bool=False,
32
+ ):
33
+ """Establish how the file dialog will behave.
34
+
35
+ title:
36
+ The Dialog Window name. Defaults to "Open File".
37
+ initial_dir:
38
+ The directory to start in. If a path is not given, it is up the OS to determine behavior.
39
+ On Windows, if None is passed, it will open to the last used directory.
40
+ initial_file:
41
+ The filename to prepopulate with when opening. Not supported on Mac OS.
42
+ filetypes:
43
+ An optional list of tuples containing (name, extension) to filter by.
44
+ If none are given, all files will be shown and selectable.
45
+ For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
46
+ For multiple file types in the same selection, separate by a semicolon.
47
+ For example: [("Images", ".png;.bmp")]`
48
+ multiple: bool
49
+ True if multiple files can be selected. Defaults to False.
50
+ """
51
+ self.title = title
52
+ self.initial_dir = initial_dir
53
+ self.filetypes = filetypes
54
+ self.multiple = multiple
55
+ self.initial_file = initial_file
56
+
57
+ def open(self):
58
+ raise NotImplementedError
59
+
60
+ FileOpenDialogBase.register_event_type('on_dialog_open')
61
+
62
+ class FileSaveDialogBase(_Dialog):
63
+
64
+ def __init__(self, title="Save As", initial_dir: str | Path | None=None, initial_file=None, filetypes=None, default_ext=""):
65
+ """Establish how the save file dialog will behave.
66
+
67
+ title:
68
+ The Dialog Window name. Defaults to "Save As".
69
+ initial_dir:
70
+ The directory to start in. If a path is not given, it is up the OS to determine behavior.
71
+ On Windows, if None is passed, it will open to the last used directory.
72
+ initial_file:
73
+ A default file name to be filled in. Defaults to None.
74
+ filetypes:
75
+ An optional list of tuples containing (name, extension) to
76
+ filter to. If the `default_ext` argument is not given, this list
77
+ also dictates the extension that will be added to the entered
78
+ file name. If a list of `filetypes` are not give, you can enter
79
+ any file name to save as.
80
+ For example: `[("PNG", ".png"), ("24-bit Bitmap", ".bmp")]`
81
+ default_ext:
82
+ A default file extension to add to the file. This will override
83
+ the `filetypes` list if given, but will not override a manually
84
+ entered extension.
85
+ """
86
+ self.title = title
87
+ self.initial_dir = initial_dir
88
+ self.filetypes = filetypes
89
+ self.initial_file = initial_file
90
+ self.default_ext = default_ext
91
+
92
+ def open(self) -> None:
93
+ raise NotImplementedError
94
+
95
+ def on_dialog_save(self, filename):
96
+ """Event for filename choice"""
97
+
98
+
99
+ FileSaveDialogBase.register_event_type('on_dialog_save')