pyglet 2.1.5__py3-none-any.whl → 2.1.8__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 +27 -42
- pyglet/app/base.py +2 -2
- pyglet/clock.py +1 -1
- pyglet/display/base.py +31 -21
- pyglet/display/cocoa.py +25 -1
- pyglet/display/headless.py +1 -1
- pyglet/display/win32.py +134 -18
- pyglet/display/xlib.py +285 -70
- pyglet/event.py +17 -1
- pyglet/experimental/README.md +1 -1
- pyglet/experimental/jobs.py +1 -1
- pyglet/experimental/multitexture_sprite.py +2 -2
- pyglet/font/__init__.py +1 -1
- pyglet/font/base.py +8 -5
- pyglet/font/dwrite/__init__.py +13 -8
- pyglet/font/dwrite/dwrite_lib.py +1 -1
- pyglet/font/user.py +1 -1
- pyglet/gl/base.py +8 -4
- pyglet/gl/cocoa.py +4 -0
- pyglet/gl/gl.py +4 -3
- pyglet/gl/gl.pyi +2320 -0
- pyglet/gl/gl_compat.py +7 -18
- pyglet/gl/gl_compat.pyi +3097 -0
- pyglet/gl/xlib.py +24 -0
- pyglet/graphics/shader.py +34 -20
- pyglet/graphics/vertexbuffer.py +1 -1
- pyglet/gui/frame.py +2 -2
- pyglet/gui/widgets.py +1 -1
- pyglet/image/__init__.py +3 -3
- pyglet/image/buffer.py +3 -3
- pyglet/input/base.py +8 -8
- pyglet/input/linux/evdev.py +1 -1
- pyglet/libs/darwin/cocoapy/cocoalibs.py +3 -1
- pyglet/libs/win32/__init__.py +12 -0
- pyglet/libs/win32/constants.py +4 -0
- pyglet/libs/win32/types.py +97 -0
- pyglet/libs/x11/xrandr.py +166 -0
- pyglet/libs/x11/xrender.py +43 -0
- pyglet/libs/x11/xsync.py +43 -0
- pyglet/math.py +40 -49
- pyglet/media/buffered_logger.py +1 -1
- pyglet/media/codecs/ffmpeg.py +18 -34
- pyglet/media/codecs/gstreamer.py +3 -3
- pyglet/media/codecs/pyogg.py +1 -1
- pyglet/media/codecs/wave.py +6 -0
- pyglet/media/codecs/wmf.py +33 -7
- pyglet/media/devices/win32.py +1 -1
- pyglet/media/drivers/base.py +1 -1
- pyglet/media/drivers/directsound/interface.py +4 -0
- pyglet/media/drivers/listener.py +2 -2
- pyglet/media/drivers/xaudio2/interface.py +6 -2
- pyglet/media/drivers/xaudio2/lib_xaudio2.py +1 -1
- pyglet/media/instrumentation.py +2 -2
- pyglet/media/player.py +2 -2
- pyglet/media/player_worker_thread.py +1 -1
- pyglet/media/synthesis.py +1 -1
- pyglet/model/codecs/gltf.py +1 -1
- pyglet/shapes.py +25 -24
- pyglet/sprite.py +1 -1
- pyglet/text/caret.py +44 -5
- pyglet/text/layout/base.py +3 -3
- pyglet/util.py +1 -1
- pyglet/window/__init__.py +54 -14
- pyglet/window/cocoa/__init__.py +27 -0
- pyglet/window/mouse.py +11 -1
- pyglet/window/win32/__init__.py +40 -14
- pyglet/window/xlib/__init__.py +21 -7
- {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/METADATA +1 -1
- {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/RECORD +71 -67
- {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/LICENSE +0 -0
- {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/WHEEL +0 -0
pyglet/display/xlib.py
CHANGED
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import ctypes
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from
|
|
4
|
+
import warnings
|
|
5
|
+
from ctypes import POINTER, byref, c_buffer, c_char_p, c_int, cast
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
|
+
import pyglet
|
|
8
9
|
from pyglet import app
|
|
9
10
|
from pyglet.app.xlib import XlibSelectDevice
|
|
11
|
+
from pyglet.libs.x11 import xlib
|
|
12
|
+
from pyglet.util import asbytes
|
|
10
13
|
|
|
11
|
-
from ..libs.x11.xlib import Window
|
|
12
14
|
from . import xlib_vidmoderestore
|
|
13
15
|
from .base import Canvas, Display, Screen, ScreenMode
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# from pyglet.window import NoSuchDisplayException
|
|
18
|
-
class NoSuchDisplayException(Exception):
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from pyglet.libs.x11 import xlib
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from pyglet.gl import Config
|
|
23
19
|
|
|
24
20
|
try:
|
|
25
21
|
from pyglet.libs.x11 import xinerama
|
|
@@ -42,6 +38,17 @@ try:
|
|
|
42
38
|
except:
|
|
43
39
|
_have_xf86vmode = False
|
|
44
40
|
|
|
41
|
+
try:
|
|
42
|
+
from pyglet.libs.x11 import xrandr
|
|
43
|
+
|
|
44
|
+
_have_xrandr = True
|
|
45
|
+
except ImportError:
|
|
46
|
+
_have_xrandr = False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class NoSuchDisplayException(Exception):
|
|
50
|
+
pass
|
|
51
|
+
|
|
45
52
|
|
|
46
53
|
# Set up error handler
|
|
47
54
|
def _error_handler(display, event):
|
|
@@ -52,7 +59,6 @@ def _error_handler(display, event):
|
|
|
52
59
|
# driver bugs (and so the reports are useless). Nevertheless, set
|
|
53
60
|
# environment variable PYGLET_DEBUG_X11 to 1 to get dumps of the error
|
|
54
61
|
# and a traceback (execution will continue).
|
|
55
|
-
import pyglet
|
|
56
62
|
if pyglet.options['debug_x11']:
|
|
57
63
|
event = event.contents
|
|
58
64
|
buf = c_buffer(1024)
|
|
@@ -64,6 +70,7 @@ def _error_handler(display, event):
|
|
|
64
70
|
print(' resource:', event.resourceid)
|
|
65
71
|
|
|
66
72
|
import traceback
|
|
73
|
+
|
|
67
74
|
print('Python stack trace (innermost last):')
|
|
68
75
|
traceback.print_stack()
|
|
69
76
|
return 0
|
|
@@ -79,9 +86,11 @@ class XlibDisplay(XlibSelectDevice, Display):
|
|
|
79
86
|
_x_im = None # X input method
|
|
80
87
|
# TODO close _x_im when display connection closed.
|
|
81
88
|
_enable_xsync = False
|
|
82
|
-
_screens
|
|
89
|
+
_screens: list[XlibScreen | XlibScreenXrandr | XlibScreenXinerama]
|
|
83
90
|
|
|
84
91
|
def __init__(self, name=None, x_screen=None):
|
|
92
|
+
self._screens = []
|
|
93
|
+
|
|
85
94
|
if x_screen is None:
|
|
86
95
|
x_screen = 0
|
|
87
96
|
|
|
@@ -107,54 +116,95 @@ class XlibDisplay(XlibSelectDevice, Display):
|
|
|
107
116
|
if _have_xsync:
|
|
108
117
|
event_base = c_int()
|
|
109
118
|
error_base = c_int()
|
|
110
|
-
if xsync.XSyncQueryExtension(self._display,
|
|
111
|
-
byref(event_base),
|
|
112
|
-
byref(error_base)):
|
|
119
|
+
if xsync.XSyncQueryExtension(self._display, byref(event_base), byref(error_base)):
|
|
113
120
|
major_version = c_int()
|
|
114
121
|
minor_version = c_int()
|
|
115
|
-
if xsync.XSyncInitialize(self._display,
|
|
116
|
-
byref(major_version),
|
|
117
|
-
byref(minor_version)):
|
|
122
|
+
if xsync.XSyncInitialize(self._display, byref(major_version), byref(minor_version)):
|
|
118
123
|
self._enable_xsync = True
|
|
119
124
|
|
|
120
125
|
# Add to event loop select list. Assume we never go away.
|
|
121
126
|
app.platform_event_loop.select_devices.add(self)
|
|
122
127
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
def get_default_screen(self) -> Screen:
|
|
129
|
+
screens = self.get_screens()
|
|
130
|
+
if _have_xrandr:
|
|
131
|
+
for screen in screens:
|
|
132
|
+
if screen.is_primary:
|
|
133
|
+
return screen
|
|
134
|
+
|
|
135
|
+
# Couldn't find a default screen, use the first in the list.
|
|
136
|
+
return self._screens[0]
|
|
137
|
+
|
|
138
|
+
def get_screens(self) -> list[XlibScreen]:
|
|
139
|
+
self._screens = []
|
|
140
|
+
|
|
141
|
+
# Use XRandr if available, as it appears more maintained and widely supported.
|
|
142
|
+
if _have_xrandr:
|
|
143
|
+
root = xlib.XDefaultRootWindow(self._display)
|
|
144
|
+
|
|
145
|
+
res_ptr = xrandr.XRRGetScreenResources(self._display, root)
|
|
146
|
+
if res_ptr:
|
|
147
|
+
primary = xrandr.XRRGetOutputPrimary(self._display, root)
|
|
148
|
+
|
|
149
|
+
res = res_ptr.contents
|
|
150
|
+
for i in range(res.noutput):
|
|
151
|
+
output_id = res.outputs[i]
|
|
152
|
+
output_info_ptr = xrandr.XRRGetOutputInfo(self._display, res_ptr, output_id)
|
|
153
|
+
if not output_info_ptr:
|
|
154
|
+
xrandr.XRRFreeOutputInfo(output_info_ptr)
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
output_info: xrandr.XRROutputInfo = output_info_ptr.contents
|
|
158
|
+
|
|
159
|
+
crtc_info_ptr = xrandr.XRRGetCrtcInfo(self._display, res_ptr, output_info.crtc)
|
|
160
|
+
if not crtc_info_ptr:
|
|
161
|
+
xrandr.XRRFreeCrtcInfo(crtc_info_ptr)
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
crtc_info: xrandr.XRRCrtcInfo = crtc_info_ptr.contents
|
|
165
|
+
|
|
166
|
+
self._screens.append(
|
|
167
|
+
XlibScreenXrandr(
|
|
168
|
+
self,
|
|
169
|
+
crtc_info.x,
|
|
170
|
+
crtc_info.y,
|
|
171
|
+
crtc_info.width,
|
|
172
|
+
crtc_info.height,
|
|
173
|
+
output_info.crtc,
|
|
174
|
+
output_id,
|
|
175
|
+
ctypes.string_at(output_info.name, output_info.nameLen).decode(),
|
|
176
|
+
output_id == primary,
|
|
177
|
+
),
|
|
178
|
+
)
|
|
179
|
+
xrandr.XRRFreeCrtcInfo(crtc_info_ptr)
|
|
180
|
+
xrandr.XRRFreeOutputInfo(output_info_ptr)
|
|
181
|
+
|
|
182
|
+
xrandr.XRRFreeScreenResources(res_ptr)
|
|
183
|
+
|
|
184
|
+
if not self._screens and _have_xinerama and xinerama.XineramaIsActive(self._display):
|
|
128
185
|
number = c_int()
|
|
129
186
|
infos = xinerama.XineramaQueryScreens(self._display, byref(number))
|
|
130
187
|
infos = cast(infos, POINTER(xinerama.XineramaScreenInfo * number.value)).contents
|
|
131
|
-
|
|
188
|
+
|
|
132
189
|
using_xinerama = number.value > 1
|
|
133
|
-
for info in infos:
|
|
134
|
-
self._screens.append(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
info.width,
|
|
138
|
-
info.height,
|
|
139
|
-
using_xinerama))
|
|
190
|
+
for idx, info in enumerate(infos):
|
|
191
|
+
self._screens.append(
|
|
192
|
+
XlibScreenXinerama(self, info.x_org, info.y_org, info.width, info.height, using_xinerama, idx),
|
|
193
|
+
)
|
|
140
194
|
xlib.XFree(infos)
|
|
141
|
-
|
|
195
|
+
elif not self._screens:
|
|
142
196
|
# No xinerama
|
|
143
197
|
screen_info = xlib.XScreenOfDisplay(self._display, self.x_screen)
|
|
144
|
-
screen = XlibScreen(self,
|
|
145
|
-
0, 0,
|
|
146
|
-
screen_info.contents.width,
|
|
147
|
-
screen_info.contents.height,
|
|
148
|
-
False)
|
|
198
|
+
screen = XlibScreen(self, 0, 0, screen_info.contents.width, screen_info.contents.height)
|
|
149
199
|
self._screens = [screen]
|
|
150
200
|
return self._screens
|
|
151
201
|
|
|
152
202
|
# XlibSelectDevice interface
|
|
153
203
|
|
|
154
|
-
def fileno(self):
|
|
204
|
+
def fileno(self) -> int:
|
|
155
205
|
return self._fileno
|
|
156
206
|
|
|
157
|
-
def select(self):
|
|
207
|
+
def select(self) -> None:
|
|
158
208
|
e = xlib.XEvent()
|
|
159
209
|
while xlib.XPending(self._display):
|
|
160
210
|
xlib.XNextEvent(self._display, e)
|
|
@@ -171,18 +221,17 @@ class XlibDisplay(XlibSelectDevice, Display):
|
|
|
171
221
|
|
|
172
222
|
dispatch(e)
|
|
173
223
|
|
|
174
|
-
def poll(self):
|
|
224
|
+
def poll(self) -> int:
|
|
175
225
|
return xlib.XPending(self._display)
|
|
176
226
|
|
|
177
227
|
|
|
178
228
|
class XlibScreen(Screen):
|
|
179
229
|
_initial_mode = None
|
|
180
230
|
|
|
181
|
-
def __init__(self, display, x, y, width, height
|
|
231
|
+
def __init__(self, display: XlibDisplay, x: int, y: int, width: int, height: int):
|
|
182
232
|
super().__init__(display, x, y, width, height)
|
|
183
|
-
self._xinerama = xinerama
|
|
184
233
|
|
|
185
|
-
def get_dpi(self):
|
|
234
|
+
def get_dpi(self) -> int:
|
|
186
235
|
resource = xlib.XResourceManagerString(self.display._display)
|
|
187
236
|
dpi = 96
|
|
188
237
|
if resource:
|
|
@@ -192,8 +241,7 @@ class XlibScreen(Screen):
|
|
|
192
241
|
if db:
|
|
193
242
|
rs_type = c_char_p()
|
|
194
243
|
value = xlib.XrmValue()
|
|
195
|
-
if xlib.XrmGetResource(db, asbytes("Xft.dpi"), asbytes("Xft.dpi"),
|
|
196
|
-
byref(rs_type), byref(value)):
|
|
244
|
+
if xlib.XrmGetResource(db, asbytes("Xft.dpi"), asbytes("Xft.dpi"), byref(rs_type), byref(value)):
|
|
197
245
|
if value.addr and rs_type.value == b'String':
|
|
198
246
|
dpi = int(value.addr)
|
|
199
247
|
|
|
@@ -201,10 +249,10 @@ class XlibScreen(Screen):
|
|
|
201
249
|
|
|
202
250
|
return dpi
|
|
203
251
|
|
|
204
|
-
def get_scale(self):
|
|
252
|
+
def get_scale(self) -> float:
|
|
205
253
|
return self.get_dpi() / 96
|
|
206
254
|
|
|
207
|
-
def get_matching_configs(self, template):
|
|
255
|
+
def get_matching_configs(self, template: Config):
|
|
208
256
|
canvas = XlibCanvas(self.display, None)
|
|
209
257
|
configs = template.match(canvas)
|
|
210
258
|
# XXX deprecate
|
|
@@ -212,41 +260,40 @@ class XlibScreen(Screen):
|
|
|
212
260
|
config.screen = self
|
|
213
261
|
return configs
|
|
214
262
|
|
|
215
|
-
def get_modes(self):
|
|
263
|
+
def get_modes(self) -> list[XlibScreenModeXF86]:
|
|
216
264
|
if not _have_xf86vmode:
|
|
217
265
|
return []
|
|
218
266
|
|
|
219
|
-
if self._xinerama:
|
|
220
|
-
# If Xinerama/TwinView is enabled, xf86vidmode's modelines
|
|
221
|
-
# correspond to metamodes, which don't distinguish one screen from
|
|
222
|
-
# another. XRandR (broken) or NV (complicated) extensions needed.
|
|
223
|
-
return []
|
|
224
|
-
|
|
225
267
|
count = ctypes.c_int()
|
|
226
268
|
info_array = ctypes.POINTER(ctypes.POINTER(xf86vmode.XF86VidModeModeInfo))()
|
|
227
269
|
xf86vmode.XF86VidModeGetAllModeLines(self.display._display, self.display.x_screen, count, info_array)
|
|
228
270
|
|
|
271
|
+
depth = xlib.XDefaultDepth(self.display._display, self.display.x_screen)
|
|
272
|
+
|
|
229
273
|
# Copy modes out of list and free list
|
|
230
274
|
modes = []
|
|
231
275
|
for i in range(count.value):
|
|
232
276
|
info = xf86vmode.XF86VidModeModeInfo()
|
|
233
|
-
ctypes.memmove(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
277
|
+
ctypes.memmove(
|
|
278
|
+
ctypes.byref(info),
|
|
279
|
+
ctypes.byref(info_array.contents[i]),
|
|
280
|
+
ctypes.sizeof(info),
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
modes.append(XlibScreenModeXF86(self, info, depth))
|
|
237
284
|
if info.privsize:
|
|
238
285
|
xlib.XFree(info.private)
|
|
239
286
|
xlib.XFree(info_array)
|
|
240
287
|
|
|
241
288
|
return modes
|
|
242
289
|
|
|
243
|
-
def get_mode(self):
|
|
290
|
+
def get_mode(self) -> XlibScreenMode:
|
|
244
291
|
modes = self.get_modes()
|
|
245
292
|
if modes:
|
|
246
293
|
return modes[0]
|
|
247
294
|
return None
|
|
248
295
|
|
|
249
|
-
def set_mode(self, mode):
|
|
296
|
+
def set_mode(self, mode: XlibScreenModeXF86):
|
|
250
297
|
assert mode.screen is self
|
|
251
298
|
|
|
252
299
|
if not self._initial_mode:
|
|
@@ -265,24 +312,192 @@ class XlibScreen(Screen):
|
|
|
265
312
|
if self._initial_mode:
|
|
266
313
|
self.set_mode(self._initial_mode)
|
|
267
314
|
|
|
315
|
+
def get_display_id(self) -> int:
|
|
316
|
+
# No real unique ID is available, just hash together the properties.
|
|
317
|
+
return hash((self.x, self.y, self.width, self.height))
|
|
318
|
+
|
|
319
|
+
def get_monitor_name(self) -> str:
|
|
320
|
+
# No way to get any screen name without XRandr or EDID information.
|
|
321
|
+
return "Unknown"
|
|
322
|
+
|
|
268
323
|
def __repr__(self):
|
|
269
|
-
return
|
|
270
|
-
|
|
324
|
+
return (
|
|
325
|
+
f"{self.__class__.__name__}(display={self.display!r}, x={self.x}, y={self.y}, "
|
|
326
|
+
f"width={self.width}, height={self.height})"
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class XlibScreenXinerama(XlibScreen):
|
|
331
|
+
def __init__(self, display: XlibDisplay, x: int, y: int, width: int, height: int, using_xinerama: bool, idx: int) -> None:
|
|
332
|
+
super().__init__(display, x, y, width, height)
|
|
333
|
+
self._xinerama = using_xinerama
|
|
334
|
+
self.idx = idx
|
|
335
|
+
|
|
336
|
+
def get_display_id(self) -> int:
|
|
337
|
+
# No real unique ID is available, just hash together the properties.
|
|
338
|
+
return hash((self.idx, self.x, self.y, self.width, self.height))
|
|
339
|
+
|
|
340
|
+
def get_modes(self):
|
|
341
|
+
if self._xinerama:
|
|
342
|
+
# If Xinerama/TwinView is enabled, xf86vidmode's modelines
|
|
343
|
+
# correspond to metamodes, which don't distinguish one screen from
|
|
344
|
+
# another. XRandR (broken) or NV (complicated) extensions needed.
|
|
345
|
+
return []
|
|
346
|
+
|
|
347
|
+
return super().get_modes()
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class XlibScreenXrandr(XlibScreen):
|
|
351
|
+
def __init__(
|
|
352
|
+
self,
|
|
353
|
+
display: XlibDisplay,
|
|
354
|
+
x: int,
|
|
355
|
+
y: int,
|
|
356
|
+
width: int,
|
|
357
|
+
height: int,
|
|
358
|
+
crtc_id: int,
|
|
359
|
+
output_id: int,
|
|
360
|
+
name: str,
|
|
361
|
+
is_primary: bool,
|
|
362
|
+
):
|
|
363
|
+
super().__init__(display, x, y, width, height)
|
|
364
|
+
self.crtc_id = crtc_id
|
|
365
|
+
self.output_id = output_id
|
|
366
|
+
self.name = name
|
|
367
|
+
self._is_primary = is_primary
|
|
368
|
+
|
|
369
|
+
@property
|
|
370
|
+
def is_primary(self):
|
|
371
|
+
return self._is_primary
|
|
372
|
+
|
|
373
|
+
@staticmethod
|
|
374
|
+
def _get_mode_info(resource: xrandr.XRRScreenResources, rrmode_id: int) -> xrandr.XRRModeInfo | None:
|
|
375
|
+
for i in range(resource.nmode):
|
|
376
|
+
if resource.modes[i].id == rrmode_id:
|
|
377
|
+
return resource.modes[i]
|
|
378
|
+
|
|
379
|
+
return None
|
|
380
|
+
|
|
381
|
+
def set_mode(self, mode: XlibScreenModeXrandr) -> None:
|
|
382
|
+
assert mode.screen is self
|
|
383
|
+
|
|
384
|
+
if not self._initial_mode:
|
|
385
|
+
self._initial_mode = self.get_mode()
|
|
386
|
+
|
|
387
|
+
root = xlib.XDefaultRootWindow(self.display._display)
|
|
388
|
+
res_ptr = xrandr.XRRGetScreenResourcesCurrent(self.display._display, root)
|
|
389
|
+
if res_ptr:
|
|
390
|
+
crtc_info_ptr = xrandr.XRRGetCrtcInfo(self.display._display, res_ptr, self.crtc_id)
|
|
391
|
+
if crtc_info_ptr:
|
|
392
|
+
crtc_info = crtc_info_ptr.contents
|
|
393
|
+
status = xrandr.XRRSetCrtcConfig(
|
|
394
|
+
self.display._display,
|
|
395
|
+
res_ptr,
|
|
396
|
+
self.crtc_id,
|
|
397
|
+
xlib.CurrentTime,
|
|
398
|
+
crtc_info.x,
|
|
399
|
+
crtc_info.y,
|
|
400
|
+
mode.mode_id,
|
|
401
|
+
crtc_info.rotation,
|
|
402
|
+
crtc_info.outputs,
|
|
403
|
+
crtc_info.noutput,
|
|
404
|
+
)
|
|
405
|
+
if status != 0 and pyglet.options['debug_x11']:
|
|
406
|
+
warnings.warn(f"Could not set screen mode: {status}")
|
|
407
|
+
xlib.XFlush(self.display._display)
|
|
408
|
+
|
|
409
|
+
self.width = mode.width
|
|
410
|
+
self.height = mode.height
|
|
411
|
+
|
|
412
|
+
xrandr.XRRFreeCrtcInfo(crtc_info_ptr)
|
|
413
|
+
xrandr.XRRFreeScreenResources(res_ptr)
|
|
414
|
+
|
|
415
|
+
def get_modes(self) -> list[XlibScreenModeXrandr]:
|
|
416
|
+
modes = []
|
|
417
|
+
root = xlib.XDefaultRootWindow(self.display._display)
|
|
418
|
+
res_ptr = xrandr.XRRGetScreenResourcesCurrent(self.display._display, root)
|
|
419
|
+
output_info_ptr = xrandr.XRRGetOutputInfo(self.display._display, res_ptr, self.output_id)
|
|
420
|
+
|
|
421
|
+
res = res_ptr.contents
|
|
422
|
+
output_info = output_info_ptr.contents
|
|
423
|
+
|
|
424
|
+
depth = xlib.XDefaultDepth(self.display._display, self.display.x_screen)
|
|
425
|
+
|
|
426
|
+
for i in range(output_info_ptr.contents.nmode):
|
|
427
|
+
mode_id = output_info.modes[i]
|
|
428
|
+
xrandr_mode = self._get_mode_info(res, mode_id)
|
|
429
|
+
if xrandr_mode:
|
|
430
|
+
mode = XlibScreenModeXrandr(self, xrandr_mode, mode_id, depth)
|
|
431
|
+
modes.append(mode)
|
|
432
|
+
|
|
433
|
+
xrandr.XRRFreeOutputInfo(output_info_ptr)
|
|
434
|
+
xrandr.XRRFreeScreenResources(res_ptr)
|
|
435
|
+
return modes
|
|
436
|
+
|
|
437
|
+
def get_mode(self) -> XlibScreenModeXrandr | None:
|
|
438
|
+
# Return the current mode.
|
|
439
|
+
root = xlib.XDefaultRootWindow(self.display._display)
|
|
440
|
+
res_ptr = xrandr.XRRGetScreenResourcesCurrent(self.display._display, root)
|
|
441
|
+
crtc_info_ptr = xrandr.XRRGetCrtcInfo(self.display._display, res_ptr, self.crtc_id)
|
|
442
|
+
|
|
443
|
+
crtc_info = crtc_info_ptr.contents
|
|
444
|
+
found_mode = None
|
|
445
|
+
|
|
446
|
+
for mode in self.get_modes():
|
|
447
|
+
if crtc_info.mode == mode.mode_id:
|
|
448
|
+
found_mode = mode
|
|
449
|
+
break
|
|
450
|
+
|
|
451
|
+
xrandr.XRRFreeCrtcInfo(crtc_info_ptr)
|
|
452
|
+
xrandr.XRRFreeScreenResources(res_ptr)
|
|
453
|
+
return found_mode
|
|
454
|
+
|
|
455
|
+
def get_display_id(self) -> int:
|
|
456
|
+
return self.output_id
|
|
457
|
+
|
|
458
|
+
def get_monitor_name(self) -> str:
|
|
459
|
+
return self.name
|
|
271
460
|
|
|
272
461
|
|
|
273
462
|
class XlibScreenMode(ScreenMode):
|
|
274
|
-
def __init__(self, screen,
|
|
463
|
+
def __init__(self, screen: XlibScreen, width: int, height: int, rate: int, depth: int):
|
|
275
464
|
super().__init__(screen)
|
|
465
|
+
self.width = width
|
|
466
|
+
self.height = height
|
|
467
|
+
self.rate = rate
|
|
468
|
+
self.depth = depth
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class XlibScreenModeXF86(XlibScreenMode):
|
|
472
|
+
def __init__(self, screen: XlibScreen, info: xf86vmode.XF86VidModeModeInfo, depth: int) -> None:
|
|
276
473
|
self.info = info
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
474
|
+
width = info.hdisplay
|
|
475
|
+
height = info.vdisplay
|
|
476
|
+
rate = round((info.dotclock * 1000) / (info.htotal * info.vtotal))
|
|
477
|
+
super().__init__(screen, width, height, rate, depth)
|
|
478
|
+
|
|
479
|
+
def __repr__(self) -> str:
|
|
480
|
+
return f'XlibScreenMode(width={self.width!r}, height={self.height!r}, depth={self.depth!r}, rate={self.rate})'
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
class XlibScreenModeXrandr(XlibScreenMode):
|
|
484
|
+
def __init__(self, screen: XlibScreen, mode_info: xrandr.XRRModeInfo, mode_id: int, depth: int) -> None:
|
|
485
|
+
self.mode_id = mode_id
|
|
486
|
+
super().__init__(screen, mode_info.width, mode_info.height, self._calculate_refresh_rate(mode_info), depth)
|
|
487
|
+
|
|
488
|
+
@staticmethod
|
|
489
|
+
def _calculate_refresh_rate(mode_info: xrandr.XRRModeInfo) -> int:
|
|
490
|
+
if mode_info.hTotal > 0 and mode_info.vTotal > 0:
|
|
491
|
+
return round(mode_info.dotClock / (mode_info.hTotal * mode_info.vTotal))
|
|
492
|
+
return 0
|
|
493
|
+
|
|
494
|
+
def __repr__(self) -> str:
|
|
495
|
+
return f'XlibScreenMode(width={self.width!r}, height={self.height!r}, depth={self.depth!r}, rate={self.rate})'
|
|
281
496
|
|
|
282
497
|
|
|
283
498
|
class XlibCanvas(Canvas): # noqa: D101
|
|
284
499
|
display: XlibDisplay
|
|
285
500
|
|
|
286
|
-
def __init__(self, display: XlibDisplay, x_window: Window) -> None: # noqa: D107
|
|
501
|
+
def __init__(self, display: XlibDisplay, x_window: xlib.Window) -> None: # noqa: D107
|
|
287
502
|
super().__init__(display)
|
|
288
503
|
self.x_window = x_window
|
pyglet/event.py
CHANGED
|
@@ -120,10 +120,13 @@ from __future__ import annotations
|
|
|
120
120
|
|
|
121
121
|
import inspect
|
|
122
122
|
import os.path
|
|
123
|
+
|
|
123
124
|
from functools import partial
|
|
124
125
|
from typing import TYPE_CHECKING, Literal, Union
|
|
125
126
|
from weakref import WeakMethod
|
|
126
127
|
|
|
128
|
+
import pyglet
|
|
129
|
+
|
|
127
130
|
if TYPE_CHECKING:
|
|
128
131
|
from typing import Any, Callable, Generator
|
|
129
132
|
|
|
@@ -328,7 +331,7 @@ class EventDispatcher:
|
|
|
328
331
|
handlers down the stack will receive this event.
|
|
329
332
|
|
|
330
333
|
This method has several possible return values. If any event
|
|
331
|
-
|
|
334
|
+
handler has returned ``EVENT_HANDLED``, then this method will
|
|
332
335
|
also return ``EVENT_HANDLED``. If not, this method will return
|
|
333
336
|
``EVENT_UNHANDLED``. If there were no events registered to
|
|
334
337
|
receive this event, ``False`` is returned.
|
|
@@ -381,6 +384,19 @@ class EventDispatcher:
|
|
|
381
384
|
|
|
382
385
|
return False
|
|
383
386
|
|
|
387
|
+
def post_event(self, event_type: str, *args: Any) -> bool | None:
|
|
388
|
+
"""Post an event to the main application thread.
|
|
389
|
+
|
|
390
|
+
Unlike the :py:meth:`~pyglet.event.EventDispatcher.dispatch_event`
|
|
391
|
+
method, this method does not dispatch events directly. Instead, it
|
|
392
|
+
hands off the dispatch call to the main application thread. This
|
|
393
|
+
ensures that any event handlers are also executed in the main thread.
|
|
394
|
+
|
|
395
|
+
This method aliases :py:meth:`~pyglet.app.PlatformEventLoop.post_event`,
|
|
396
|
+
which can be seen for more information on behavior.
|
|
397
|
+
"""
|
|
398
|
+
pyglet.app.platform_event_loop.post_event(self, event_type, *args)
|
|
399
|
+
|
|
384
400
|
def _raise_dispatch_exception(self, event_type: str, args: Any, handler: Callable, exception: Exception) -> None:
|
|
385
401
|
# A common problem in applications is having the wrong number of
|
|
386
402
|
# arguments in an event handler. This is caught as a TypeError in
|
pyglet/experimental/README.md
CHANGED
|
@@ -2,5 +2,5 @@ Experimental modules
|
|
|
2
2
|
====================
|
|
3
3
|
|
|
4
4
|
This package contains experimental modules, which are included here for
|
|
5
|
-
wider testing and feedback. Anything
|
|
5
|
+
wider testing and feedback. Anything contained within may be broken, refactored,
|
|
6
6
|
or removed without notice.
|
pyglet/experimental/jobs.py
CHANGED
|
@@ -92,7 +92,7 @@ class JobExecutor:
|
|
|
92
92
|
|
|
93
93
|
All JobExecutor workers are Daemon Threads, so it is not strictly
|
|
94
94
|
necessary to call shutdown() at program termination. However, if
|
|
95
|
-
it is no longer needed, shutdown() can be called
|
|
95
|
+
it is no longer needed, shutdown() can be called to free up the
|
|
96
96
|
thread resources.
|
|
97
97
|
"""
|
|
98
98
|
if not self._queue:
|
|
@@ -378,7 +378,7 @@ class MultiTextureSprite(pyglet.sprite.Sprite):
|
|
|
378
378
|
if new_tex.id is not self._textures[key].id:
|
|
379
379
|
# Need to make a shallow copy to allow the batch object
|
|
380
380
|
# to correctly split this sprite from other sprite's groups.
|
|
381
|
-
# if not then you will be
|
|
381
|
+
# if not then you will be modifying all the other sprites
|
|
382
382
|
# textures dict object as well.
|
|
383
383
|
self._textures = self._textures.copy()
|
|
384
384
|
self._textures[key] = new_tex
|
|
@@ -467,7 +467,7 @@ class MultiTextureSprite(pyglet.sprite.Sprite):
|
|
|
467
467
|
The Image or Animation to set
|
|
468
468
|
"""
|
|
469
469
|
if name in self._animations:
|
|
470
|
-
# Need to stop all animations
|
|
470
|
+
# Need to stop all animations temporarily so we can swap the layer out
|
|
471
471
|
pyglet.clock.unschedule(self._animate)
|
|
472
472
|
self._animations.pop(name)
|
|
473
473
|
|
pyglet/font/__init__.py
CHANGED
|
@@ -68,7 +68,7 @@ def add_user_font(font: UserDefinedFontBase) -> None:
|
|
|
68
68
|
Exception: If font provided is not derived from :py:class:`~pyglet.font.user.UserDefinedFontBase`.
|
|
69
69
|
"""
|
|
70
70
|
if not isinstance(font, UserDefinedFontBase):
|
|
71
|
-
msg = "Font
|
|
71
|
+
msg = "Font must be created from the UserDefinedFontBase."
|
|
72
72
|
raise Exception(msg)
|
|
73
73
|
|
|
74
74
|
# Locate or create font cache
|
pyglet/font/base.py
CHANGED
|
@@ -7,10 +7,9 @@ classes as a documented interface to the concrete classes.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import abc
|
|
10
|
-
from dataclasses import dataclass
|
|
11
|
-
|
|
12
10
|
import unicodedata
|
|
13
|
-
from
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any, BinaryIO, ClassVar
|
|
14
13
|
|
|
15
14
|
from pyglet import image
|
|
16
15
|
from pyglet.gl import GL_LINEAR, GL_RGBA, GL_TEXTURE_2D
|
|
@@ -63,7 +62,7 @@ def grapheme_break(left: str, left_cc: str, right: str, right_cc: str) -> bool:
|
|
|
63
62
|
return False
|
|
64
63
|
|
|
65
64
|
# GB9b: Do not break after Prepend characters
|
|
66
|
-
if left in _LOGICAL_ORDER_EXCEPTION:
|
|
65
|
+
if left in _LOGICAL_ORDER_EXCEPTION:
|
|
67
66
|
return False
|
|
68
67
|
|
|
69
68
|
# GB999: Default to break
|
|
@@ -259,7 +258,11 @@ class Font:
|
|
|
259
258
|
``GL_NEAREST`` to prevent aliasing with pixelated fonts.
|
|
260
259
|
"""
|
|
261
260
|
#: :meta private:
|
|
262
|
-
glyphs: dict[str | int, Glyph]
|
|
261
|
+
glyphs: dict[str | int | tuple[Any, int], Glyph]
|
|
262
|
+
# Glyphs can be cached in various ways:
|
|
263
|
+
# str: if no text shaping
|
|
264
|
+
# int: glyph index, if no fallback behavior.
|
|
265
|
+
# tuple: with a unique font identifier and glyph index
|
|
263
266
|
|
|
264
267
|
texture_width: int = 512
|
|
265
268
|
texture_height: int = 512
|