update pyglet

This commit is contained in:
shenjack 2023-01-25 20:38:17 +08:00
parent f1a00a179d
commit e4c1b7bb9a
46 changed files with 611 additions and 281 deletions

View File

@ -128,7 +128,8 @@ options = {
'headless': False,
'headless_device': 0,
'win32_disable_shaping': False,
'dw_legacy_naming': False
'dw_legacy_naming': False,
'win32_disable_xinput': False,
}
_option_types = {
@ -158,14 +159,15 @@ _option_types = {
'headless': bool,
'headless_device': int,
'win32_disable_shaping': bool,
'dw_legacy_naming': bool
'dw_legacy_naming': bool,
'win32_disable_xinput': bool
}
for key in options:
"""Read defaults for options from environment"""
assert key in _option_types, f"Option '{key}' must have a type set in _option_types."
env = 'PYGLET_%s' % key.upper()
env = f'PYGLET_{key.upper()}'
try:
value = os.environ[env]
if _option_types[key] is tuple:
@ -232,21 +234,21 @@ def _trace_frame(thread, frame, indent):
filename = os.path.join('...', filename)
_trace_filename_abbreviations[path] = filename
location = '(%s:%d)' % (filename, line)
location = f'({filename}:{line})'
if indent:
name = 'Called from %s' % name
print('[%d] %s%s %s' % (thread, indent, name, location))
name = f'Called from {name}'
print(f'[{thread}] {indent}{name} {location}')
if _trace_args:
if is_ctypes:
args = [_trace_repr(arg) for arg in frame.f_locals['args']]
print(' %sargs=(%s)' % (indent, ', '.join(args)))
print(f' {indent}args=({", ".join(args)})')
else:
for argname in code.co_varnames[:code.co_argcount]:
try:
argvalue = _trace_repr(frame.f_locals[argname])
print(' %s%s=%s' % (indent, argname, argvalue))
print(f' {indent}{argname}={argvalue}')
except:
pass
@ -302,7 +304,7 @@ class _ModuleProxy:
if self._module is not None:
raise
import_name = 'pyglet.%s' % self._module_name
import_name = f'pyglet.{self._module_name}'
__import__(import_name)
module = sys.modules[import_name]
object.__setattr__(self, '_module', module)
@ -316,7 +318,7 @@ class _ModuleProxy:
if self._module is not None:
raise
import_name = 'pyglet.%s' % self._module_name
import_name = f'pyglet.{self._module_name}'
__import__(import_name)
module = sys.modules[import_name]
object.__setattr__(self, '_module', module)

View File

@ -311,9 +311,7 @@ class ScreenMode:
self.screen = screen
def __repr__(self):
return '%s(width=%r, height=%r, depth=%r, rate=%r)' % (
self.__class__.__name__,
self.width, self.height, self.depth, self.rate)
return f'{self.__class__.__name__}(width={self.width!r}, height={self.height!r}, depth={self.depth!r}, rate={self.rate})'
class Canvas:

View File

@ -21,7 +21,7 @@ class HeadlessDisplay(Display):
if num_devices.value > 0:
headless_device = pyglet.options['headless_device']
if headless_device < 0 or headless_device >= num_devices.value:
raise ValueError('Invalid EGL devide id: %d' % headless_device)
raise ValueError(f'Invalid EGL devide id: {headless_device}')
devices = (eglext.EGLDeviceEXT * num_devices.value)()
eglext.eglQueryDevicesEXT(num_devices.value, devices, byref(num_devices))
self._display_connection = eglext.eglGetPlatformDisplayEXT(

View File

@ -99,9 +99,7 @@ class Win32ScreenMode(ScreenMode):
self.scaling = mode.dmDisplayFixedOutput
def __repr__(self):
return '%s(width=%r, height=%r, depth=%r, rate=%r, scaling=%r)' % (
self.__class__.__name__,
self.width, self.height, self.depth, self.rate, self.scaling)
return f'{self.__class__.__name__}(width={self.width!r}, height={self.height!r}, depth={self.depth!r}, rate={self.rate}, scaling={self.scaling})'
class Win32Canvas(Canvas):
def __init__(self, display, hwnd, hdc):

View File

@ -85,11 +85,11 @@ class XlibDisplay(XlibSelectDevice, Display):
self._display = xlib.XOpenDisplay(name)
if not self._display:
raise NoSuchDisplayException('Cannot connect to "%s"' % name)
raise NoSuchDisplayException(f'Cannot connect to "{name}"')
screen_count = xlib.XScreenCount(self._display)
if x_screen >= screen_count:
raise NoSuchDisplayException('Display "%s" has no screen %d' % (name, x_screen))
raise NoSuchDisplayException(f'Display "{name}" has no screen {x_screen:d}')
super(XlibDisplay, self).__init__()
self.name = name
@ -239,9 +239,8 @@ class XlibScreen(Screen):
self.set_mode(self._initial_mode)
def __repr__(self):
return 'XlibScreen(display=%r, x=%d, y=%d, ' \
'width=%d, height=%d, xinerama=%d)' % \
(self.display, self.x, self.y, self.width, self.height, self._xinerama)
return f"{self.__class__.__name__}(display={self.display!r}, x={self.x}, y={self.y}, " \
f"width={self.width}, height={self.height}, xinerama={self._xinerama})"

View File

@ -58,9 +58,8 @@ class ModePacket:
return cls(display.strip(asbytes('\0')), screen, width, height, rate)
def __repr__(self):
return '%s(%r, %r, %r, %r, %r)' % (
self.__class__.__name__, self.display, self.screen,
self.width, self.height, self.rate)
return f'{self.__class__.__name__}({self.display}, ' \
f'{self.screen}, {self.width}, {self.height}, {self.rate})'
def set(self):
display = xlib.XOpenDisplay(self.display)

View File

@ -184,7 +184,7 @@ class EventDispatcher:
# Single magically named function
name = obj.__name__
if name not in self.event_types:
raise EventException('Unknown event "%s"' % name)
raise EventException(f'Unknown event "{name}"')
if inspect.ismethod(obj):
yield name, WeakMethod(obj, partial(self._remove_handler, name))
else:
@ -199,7 +199,7 @@ class EventDispatcher:
for name, handler in kwargs.items():
# Function for handling given event (no magic)
if name not in self.event_types:
raise EventException('Unknown event "%s"' % name)
raise EventException(f'Unknown event "{name}"')
if inspect.ismethod(handler):
yield name, WeakMethod(handler, partial(self._remove_handler, name))
else:
@ -363,7 +363,7 @@ class EventDispatcher:
"EventDispatcher.register_event_type('event_name')."
)
assert event_type in self.event_types, \
"%r not found in %r.event_types == %r" % (event_type, self, self.event_types)
f"{event_type} not found in {self}.event_types == {self.event_types}"
invoked = False

View File

@ -254,17 +254,17 @@ def check_palette(palette):
for i, t in enumerate(p):
if len(t) not in (3, 4):
raise ProtocolError(
"palette entry %d: entries must be 3- or 4-tuples." % i)
f"palette entry {i}: entries must be 3- or 4-tuples.")
if len(t) == 3:
seen_triple = True
if seen_triple and len(t) == 4:
raise ProtocolError(
"palette entry %d: all 4-tuples must precede all 3-tuples" % i)
f"palette entry {i}: all 4-tuples must precede all 3-tuples")
for x in t:
if int(x) != x or not(0 <= x <= 255):
raise ProtocolError(
"palette entry %d: "
"values must be integer: 0 <= x <= 255" % i)
f"palette entry {i}: "
"values must be integer: 0 <= x <= 255")
return p
@ -282,12 +282,10 @@ def check_sizes(size, width, height):
"size argument should be a pair (width, height)")
if width is not None and width != size[0]:
raise ProtocolError(
"size[0] (%r) and width (%r) should match when both are used."
% (size[0], width))
f"size[0] ({size[0]}) and width ({width}) should match when both are used.")
if height is not None and height != size[1]:
raise ProtocolError(
"size[1] (%r) and height (%r) should match when both are used."
% (size[1], height))
f"size[1] ({size[1]}) and height ({height}) should match when both are used.")
return size
@ -307,17 +305,17 @@ def check_color(c, greyscale, which):
except TypeError:
c = (c,)
if len(c) != 1:
raise ProtocolError("%s for greyscale must be 1-tuple" % which)
raise ProtocolError(f"{which} for greyscale must be 1-tuple")
if not is_natural(c[0]):
raise ProtocolError(
"%s colour for greyscale must be integer" % which)
f"{which} colour for greyscale must be integer")
else:
if not (len(c) == 3 and
is_natural(c[0]) and
is_natural(c[1]) and
is_natural(c[2])):
raise ProtocolError(
"%s colour must be a triple of integers" % which)
f"{which} colour must be a triple of integers")
return c
@ -553,8 +551,7 @@ class Writer:
valid = is_natural(b) and 1 <= b <= 16
if not valid:
raise ProtocolError(
"each bitdepth %r must be a positive integer <= 16" %
(bitdepth,))
f"each bitdepth {bitdepth} must be a positive integer <= 16")
# Calculate channels, and
# expand bitdepth to be one element per channel.
@ -656,8 +653,7 @@ class Writer:
if wrong_length:
# Note: row numbers start at 0.
raise ProtocolError(
"Expected %d values but got %d values, in row %d" %
(vpr, len(row), i))
f"Expected {vpr} values but got {len(row)} values, in row {i}")
yield row
if self.interlace:
@ -668,8 +664,7 @@ class Writer:
nrows = self.write_passes(outfile, check_rows(rows))
if nrows != self.height:
raise ProtocolError(
"rows supplied (%d) does not match height (%d)" %
(nrows, self.height))
f"rows supplied ({nrows}) does not match height ({self.height})")
return nrows
def write_passes(self, outfile, rows):
@ -780,8 +775,7 @@ class Writer:
if self.rescale:
write_chunk(
outfile, b'sBIT',
struct.pack('%dB' % self.planes,
* [s[0] for s in self.rescale]))
struct.pack(f'{self.planes,* [s[0] for s in self.rescale]}B' ))
# :chunk:order: Without a palette (PLTE chunk),
# ordering is relatively relaxed.
@ -997,7 +991,7 @@ def unpack_rows(rows):
to being a sequence of bytes.
"""
for row in rows:
fmt = '!%dH' % len(row)
fmt = f'!{len(row)}'
yield bytearray(struct.pack(fmt, *row))
@ -1187,8 +1181,7 @@ def from_array(a, mode=None, info={}):
if bitdepth:
if info.get("bitdepth") and bitdepth != info['bitdepth']:
raise ProtocolError(
"bitdepth (%d) should match bitdepth of info (%d)." %
(bitdepth, info['bitdepth']))
f"bitdepth ({bitdepth}) should match bitdepth of info ({info[bitdepth]}).")
info['bitdepth'] = bitdepth
# Fill in and/or check entries in *info*.
@ -1383,19 +1376,17 @@ class Reader:
data = self.file.read(length)
if len(data) != length:
raise ChunkError(
'Chunk %s too short for required %i octets.'
% (type, length))
f'Chunk {type} too short for required {length} octets.')
checksum = self.file.read(4)
if len(checksum) != 4:
raise ChunkError('Chunk %s too short for checksum.' % type)
raise ChunkError(f'Chunk {type} too short for checksum.')
verify = zlib.crc32(type)
verify = zlib.crc32(data, verify)
verify = struct.pack('!I', verify)
if checksum != verify:
(a, ) = struct.unpack('!I', checksum)
(b, ) = struct.unpack('!I', verify)
message = ("Checksum error in %s chunk: 0x%08X != 0x%08X."
% (type.decode('ascii'), a, b))
message = f"Checksum error in {type.decode('ascii')} chunk: 0x{a:08X} != 0x{b:08X}."
if lenient:
warnings.warn(message, RuntimeWarning)
else:
@ -1539,7 +1530,7 @@ class Reader:
return bytearray(bs)
if self.bitdepth == 16:
return array('H',
struct.unpack('!%dH' % (len(bs) // 2), bs))
struct.unpack(f'!{(len(bs) // 2)}H' , bs))
assert self.bitdepth < 8
if width is None:
@ -1634,14 +1625,13 @@ class Reader:
'End of file whilst reading chunk length and type.')
length, type = struct.unpack('!I4s', x)
if length > 2 ** 31 - 1:
raise FormatError('Chunk %s is too large: %d.' % (type, length))
raise FormatError(f'Chunk {type} is too large: {length}.')
# Check that all bytes are in valid ASCII range.
# https://www.w3.org/TR/2003/REC-PNG-20031110/#5Chunk-layout
type_bytes = set(bytearray(type))
if not(type_bytes <= set(range(65, 91)) | set(range(97, 123))):
raise FormatError(
'Chunk %r has invalid Chunk Type.'
% list(type))
f'Chunk {list(type)} has invalid Chunk Type.')
return length, type
def process_chunk(self, lenient=False):
@ -1673,18 +1663,17 @@ class Reader:
if self.compression != 0:
raise FormatError(
"Unknown compression method %d" % self.compression)
f"Unknown compression method {self.compression}")
if self.filter != 0:
raise FormatError(
"Unknown filter method %d,"
f"Unknown filter method {self.filter},"
" see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ."
% self.filter)
)
if self.interlace not in (0, 1):
raise FormatError(
"Unknown interlace method %d, see "
f"Unknown interlace method {self.interlace}, see "
"http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods"
" ."
% self.interlace)
" .")
# Derived values
# http://www.w3.org/TR/PNG/#6Colour-values
@ -1733,7 +1722,7 @@ class Reader:
"PLTE chunk is required before bKGD chunk.")
self.background = struct.unpack('B', data)
else:
self.background = struct.unpack("!%dH" % self.color_planes,
self.background = struct.unpack(f"!{self.color_planes}",
data)
except struct.error:
raise FormatError("bKGD chunk has incorrect length.")
@ -1752,11 +1741,10 @@ class Reader:
else:
if self.alpha:
raise FormatError(
"tRNS chunk is not valid with colour type %d." %
self.color_type)
f"tRNS chunk is not valid with colour type {self.color_type}.")
try:
self.transparent = \
struct.unpack("!%dH" % self.color_planes, data)
struct.unpack(f"!{self.color_planes}", data)
except struct.error:
raise FormatError("tRNS chunk has incorrect length.")
@ -1989,13 +1977,12 @@ class Reader:
pixels = itertrns(pixels)
targetbitdepth = None
if self.sbit:
sbit = struct.unpack('%dB' % len(self.sbit), self.sbit)
sbit = struct.unpack(f'{len(self.sbit)}', self.sbit)
targetbitdepth = max(sbit)
if targetbitdepth > info['bitdepth']:
raise Error('sBIT chunk %r exceeds bitdepth %d' %
(sbit, self.bitdepth))
raise Error(f'sBIT chunk {sbit!r} exceeds bitdepth {self.bitdepth}')
if min(sbit) <= 0:
raise Error('sBIT chunk %r has a 0-entry' % sbit)
raise Error(f'sBIT chunk {sbit} has a 0-entry')
if targetbitdepth:
shift = info['bitdepth'] - targetbitdepth
info['bitdepth'] = targetbitdepth
@ -2181,24 +2168,23 @@ def check_bitdepth_colortype(bitdepth, colortype):
"""
if bitdepth not in (1, 2, 4, 8, 16):
raise FormatError("invalid bit depth %d" % bitdepth)
raise FormatError(f"invalid bit depth {bitdepth}")
if colortype not in (0, 2, 3, 4, 6):
raise FormatError("invalid colour type %d" % colortype)
raise FormatError(f"invalid colour type {colortype}")
# Check indexed (palettized) images have 8 or fewer bits
# per pixel; check only indexed or greyscale images have
# fewer than 8 bits per pixel.
if colortype & 1 and bitdepth > 8:
raise FormatError(
"Indexed images (colour type %d) cannot"
" have bitdepth > 8 (bit depth %d)."
f"Indexed images (colour type {bitdepth}) cannot"
f" have bitdepth > 8 (bit depth {colortype})."
" See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ."
% (bitdepth, colortype))
)
if bitdepth < 8 and colortype not in (0, 3):
raise FormatError(
"Illegal combination of bit depth (%d)"
" and colour type (%d)."
" See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ."
% (bitdepth, colortype))
f"Illegal combination of bit depth ({bitdepth})"
f" and colour type ({colortype})."
" See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 .")
def is_natural(x):

View File

@ -224,7 +224,7 @@ class FreeTypeFace:
return cls(match.face)
else:
if not match.file:
raise base.FontException('No filename for "%s"' % match.name)
raise base.FontException(f'No filename for "{match.name}"')
return cls.from_file(match.file)
@property

View File

@ -367,16 +367,16 @@ class TruetypeInfo:
# a fuckwit.
header = _read_cmap_format4Header(self._data, offset)
seg_count = header.seg_count_x2 // 2
array_size = struct.calcsize('>%dH' % seg_count)
end_count = self._read_array('>%dH' % seg_count,
array_size = struct.calcsize(f'>{seg_count}')
end_count = self._read_array(f'>{seg_count}',
offset + header.size)
start_count = self._read_array('>%dH' % seg_count,
start_count = self._read_array(f'>{seg_count}',
offset + header.size + array_size + 2)
id_delta = self._read_array('>%dh' % seg_count,
id_delta = self._read_array(f'>{seg_count}',
offset + header.size + array_size + 2 + array_size)
id_range_offset_address = \
offset + header.size + array_size + 2 + array_size + array_size
id_range_offset = self._read_array('>%dH' % seg_count,
id_range_offset = self._read_array(f'>{seg_count}',
id_range_offset_address)
character_map = {}
for i in range(0, seg_count):
@ -440,7 +440,7 @@ def _read_table(*entries):
setattr(self, pname, pvalue)
def __repr__(self):
return '{'+', '.join(['%s = %s' % (pname, pvalue) for pname, pvalue in self.pairs])+'}'
return '{'+', '.join([f'{pname} = {pvalue}' for pname, pvalue in self.pairs])+'}'
@staticmethod
def array(data, offset, count):

View File

@ -29,16 +29,16 @@ def _debug_filename(base, extension):
import os
if not os.path.exists(_debug_dir):
os.makedirs(_debug_dir)
name = '%s-%%d.%%s' % os.path.join(_debug_dir, base)
name = f'{os.path.join(_debug_dir, base)}-{{0}}.{{1}}'
num = 1
while os.path.exists(name % (num, extension)):
while os.path.exists(name.format(num, extension)):
num += 1
return name % (num, extension)
return name.format(num, extension)
def _debug_image(image, name):
filename = _debug_filename(name, 'png')
image.save(filename)
_debug('Saved image %r to %s' % (image, filename))
_debug(f'Saved image {image} to {filename}')
_debug_logfile = None
def _debug(msg):
@ -106,16 +106,16 @@ class GDIGlyphRenderer(Win32GlyphRenderer):
glyph.set_bearings(-self.font.descent, lsb, advance)
if _debug_font:
_debug('%r.render(%s)' % (self, text))
_debug('abc.abcA = %r' % abc.abcA)
_debug('abc.abcB = %r' % abc.abcB)
_debug('abc.abcC = %r' % abc.abcC)
_debug('width = %r' % width)
_debug('height = %r' % height)
_debug('lsb = %r' % lsb)
_debug('advance = %r' % advance)
_debug_image(image, 'glyph_%s' % text)
_debug_image(self.font.textures[0], 'tex_%s' % text)
_debug(f'{self}.render({text})')
_debug(f'abc.abcA = {abc.abcA}')
_debug(f'abc.abcB = {abc.abcB}')
_debug(f'abc.abcC = {abc.abcC}')
_debug(f'width = {width}')
_debug(f'height = {height}')
_debug(f'lsb = {lsb}')
_debug(f'advance = {advance}')
_debug_image(image, f'glyph_{text}')
_debug_image(self.font.textures[0], f'tex_{text}')
return glyph
@ -179,11 +179,11 @@ class GDIGlyphRenderer(Win32GlyphRenderer):
self._bitmap_height = height
if _debug_font:
_debug('%r._create_dc(%d, %d)' % (self, width, height))
_debug('_dc = %r' % self._dc)
_debug('_bitmap = %r' % self._bitmap)
_debug('pitch = %r' % pitch)
_debug('info.bmiHeader.biSize = %r' % info.bmiHeader.biSize)
_debug(f'{self}._create_dc({width}, {height})')
_debug(f'_dc = {self._dc}')
_debug(f'_bitmap = {self._bitmap}')
_debug(f'pitch = {pitch}')
_debug(f'info.bmiHeader.biSize = {info.bmiHeader.biSize}')
class Win32Font(base.Font):
glyph_renderer_class = GDIGlyphRenderer

View File

@ -358,7 +358,8 @@ def _enum_font_names(logfont, textmetricex, fonttype, param):
# if pitch == FIXED_PITCH:
if 1:
print('%s CHARSET: %3s %s' % (info, lf.lfCharSet, lf.lfFaceName))
# print('%s CHARSET: %3s %s' % (info, lf.lfCharSet, lf.lfFaceName))
print(f'{info} CHARSET: {lf.lfCharSet} {lf.lfFaceName}')
return 1 # non-0 to continue enumeration
@ -481,7 +482,7 @@ if __name__ == '__main__':
print('\n'.join(fonts))
if DEBUG:
print("Total: %s" % len(font_list()))
print(f"Total: {len(font_list())}")
# -- CHAPTER 2: WORK WITH FONT DIMENSIONS --

View File

@ -86,7 +86,7 @@ if _pyglet.options['debug_texture']:
_debug_texture_sizes[texture] = size
_debug_texture_total += size
print('%d (+%d)' % (_debug_texture_total, size))
print(f'{_debug_texture_total} (+{size})')
def _debug_texture_dealloc(texture):
@ -96,7 +96,7 @@ if _pyglet.options['debug_texture']:
del _debug_texture_sizes[texture]
_debug_texture_total -= size
print('%d (-%d)' % (_debug_texture_total, size))
print(f'{_debug_texture_total} (-{size})')
_glBindTexture = glBindTexture

View File

@ -243,7 +243,7 @@ class Context:
if self.canvas is not None:
self.detach()
if not self.config.compatible(canvas):
raise RuntimeError('Cannot attach %r to %r' % (canvas, self))
raise RuntimeError(f'Cannot attach {canvas} to {self}')
self.canvas = canvas
def detach(self):

View File

@ -68,7 +68,7 @@ class GLXInfo:
minor = c_int()
if not glXQueryVersion(self.display, byref(major), byref(minor)):
raise GLXInfoException('Could not determine GLX server version')
return '%s.%s' % (major.value, minor.value)
return f'{major.value}.{minor.value}'
def get_server_extensions(self):
self.check_display()

View File

@ -57,7 +57,7 @@ def errcheck(result, func, arguments):
name = repr(func)
if _debug_gl_trace_args:
trace_args = ', '.join([repr(arg)[:20] for arg in arguments])
print('%s(%s)' % (name, trace_args))
print(f'{name}({trace_args})')
else:
print(name)
@ -76,7 +76,7 @@ def errcheck(result, func, arguments):
gl.GL_OUT_OF_MEMORY: "Out of memory. There is not enough memory left to execute the command.",
}
msg = error_types.get(error, "Unknown error")
raise GLException('(0x%x): %s' % (error, msg))
raise GLException(f'(0x{error}): {msg}')
return result

View File

@ -44,7 +44,7 @@ def dump_python():
print('os.getcwd():', os.getcwd())
for key, value in os.environ.items():
if key.startswith('PYGLET_'):
print("os.environ['%s']: %s" % (key, value))
print(f"os.environ['{key}']: {value}")
def dump_pyglet():
@ -54,7 +54,7 @@ def dump_pyglet():
print('pyglet.compat_platform:', pyglet.compat_platform)
print('pyglet.__file__:', pyglet.__file__)
for key, value in pyglet.options.items():
print("pyglet.options['%s'] = %r" % (key, value))
print(f"pyglet.options['{key}'] = {value!r}")
def dump_window():
@ -68,10 +68,10 @@ def dump_window():
print('display:', repr(display))
screens = display.get_screens()
for i, screen in enumerate(screens):
print('screens[%d]: %r' % (i, screen))
print(f'screens[{i}]: {screen!r}')
window = pyglet.window.Window(visible=False)
for key, value in window.config.get_gl_attributes():
print("config['%s'] = %r" % (key, value))
print(f"config['{key}'] = {value!r}")
print('context:', repr(window.context))
_heading('window.context._info')
@ -163,7 +163,7 @@ def dump_al():
def dump_wintab():
"""Dump WinTab info."""
try:
from pyglet.input import wintab
from pyglet.input.win32 import wintab
except:
print('WinTab not available.')
return
@ -172,7 +172,7 @@ def dump_wintab():
impl_version = wintab.get_implementation_version()
spec_version = wintab.get_spec_version()
print('WinTab: %s %d.%d (Spec %d.%d)' % (interface_name,
print('WinTab: {0} {1}.{2} (Spec {3}.{4})'.format(interface_name,
impl_version >> 8, impl_version & 0xff,
spec_version >> 8, spec_version & 0xff))

View File

@ -52,11 +52,11 @@ provides a convenient way to handle hot-plugging of controllers.
import sys
import pyglet
from .base import Device, Control, RelativeAxis, AbsoluteAxis, ControllerManager
from .base import Button, Joystick, AppleRemote, Tablet, Controller
from .base import DeviceException, DeviceOpenException, DeviceExclusiveException
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
@ -90,6 +90,7 @@ if _is_pyglet_doc_run:
:rtype: list of :py:class:`Device`
"""
def get_joysticks(display=None):
"""Get a list of attached joysticks.
@ -102,6 +103,7 @@ if _is_pyglet_doc_run:
:rtype: list of :py:class:`Joystick`
"""
def get_controllers(display=None):
"""Get a list of attached controllers.
@ -114,6 +116,7 @@ if _is_pyglet_doc_run:
:rtype: list of :py:class:`Controller`
"""
def get_tablets(display=None):
"""Get a list of tablets.
@ -133,43 +136,27 @@ if _is_pyglet_doc_run:
"""
else:
def get_tablets(display=None):
return []
from pyglet import compat_platform, options
from pyglet import compat_platform
if compat_platform.startswith('linux'):
from .x11_xinput_tablet import get_tablets
from .x11_xinput import get_devices as x11xinput_get_devices
from .evdev import get_devices as evdev_get_devices
from .evdev import get_joysticks
from .evdev import get_controllers
from .evdev import EvdevControllerManager as ControllerManager
def get_devices(display=None):
return evdev_get_devices(display) + x11xinput_get_devices(display)
from .linux import get_devices
from .linux import get_joysticks
from .linux import get_controllers
from .linux import get_tablets
from .linux import ControllerManager
elif compat_platform in ('cygwin', 'win32'):
from .directinput import get_devices as dinput_get_devices
from .directinput import get_controllers as dinput_get_controllers
from .directinput import get_joysticks
try:
from .wintab import get_tablets
except:
pass
from .xinput import get_devices as xinput_get_devices
from .xinput import get_controllers as xinput_get_controllers
from .xinput import XInputControllerManager as ControllerManager
def get_devices(display=None):
return xinput_get_devices() + dinput_get_devices(display)
def get_controllers(display=None):
return xinput_get_controllers() + dinput_get_controllers(display)
from .win32 import get_devices
from .win32 import get_joysticks
from .win32 import get_controllers
from .win32 import get_tablets
from .win32 import Win32ControllerManager as ControllerManager
elif compat_platform == 'darwin':
from .darwin_hid import get_devices
from .darwin_hid import get_joysticks
from .darwin_hid import get_apple_remote
from .darwin_hid import get_controllers
from .darwin_hid import DarwinControllerManager as ControllerManager
from .macos import get_devices
from .macos import get_joysticks
from .macos import get_apple_remote
from .macos import get_controllers
from .macos import get_tablets
from .macos import ControllerManager

View File

@ -495,6 +495,9 @@ class Joystick(EventDispatcher):
(centered) or 1 (top).
"""
def __repr__(self):
return f"Joystick(device={self.device.name})"
Joystick.register_event_type('on_joyaxis_motion')
Joystick.register_event_type('on_joybutton_press')

View File

@ -0,0 +1,10 @@
from .evdev import get_devices as evdev_get_devices
from .evdev import get_joysticks
from .evdev import get_controllers
from .evdev import EvdevControllerManager as ControllerManager
from .x11_xinput_tablet import get_tablets
from .x11_xinput import get_devices as x11xinput_get_devices
def get_devices(display=None):
return evdev_get_devices(display) + x11xinput_get_devices(display)

View File

@ -2,6 +2,7 @@ import os
import time
import fcntl
import ctypes
import warnings
from ctypes import c_uint16 as _u16
from ctypes import c_int16 as _s16
@ -14,11 +15,11 @@ from typing import List
import pyglet
from pyglet.app.xlib import XlibSelectDevice
from .base import Device, RelativeAxis, AbsoluteAxis, Button, Joystick, Controller
from .base import DeviceOpenException, ControllerManager
from .evdev_constants import *
from .controller import get_mapping, Relation, create_guid
from pyglet.app.xlib import XlibSelectDevice
from pyglet.input.base import Device, RelativeAxis, AbsoluteAxis, Button, Joystick, Controller
from pyglet.input.base import DeviceOpenException, ControllerManager
from pyglet.input.controller import get_mapping, Relation, create_guid
_IOC_NRBITS = 8
_IOC_TYPEBITS = 8
@ -637,6 +638,9 @@ def _create_controller(device):
mapping = get_mapping(device.get_guid())
if not mapping:
warnings.warn(f"Warning: {device} (GUID: {device.get_guid()}) "
f"has no controller mappings. Update the mappings in the Controller DB.\n"
f"Auto-detecting as defined by the 'Linux gamepad specification'")
mapping = _detect_controller_mapping(device)
if FF_RUMBLE in device.ff_types:

View File

@ -1,7 +1,7 @@
from pyglet.input.base import Tablet, TabletCanvas
from pyglet.input.base import TabletCursor, DeviceOpenException
from pyglet.input.x11_xinput import XInputWindowEventDispatcher
from pyglet.input.x11_xinput import get_devices, DeviceResponder
from pyglet.input.linux.x11_xinput import XInputWindowEventDispatcher
from pyglet.input.linux.x11_xinput import get_devices, DeviceResponder
try:

View File

@ -0,0 +1,11 @@
from .darwin_hid import get_devices
from .darwin_hid import get_joysticks
from .darwin_hid import get_apple_remote
from .darwin_hid import get_controllers
from .darwin_hid import DarwinControllerManager as ControllerManager
def get_tablets(display=None):
import warnings
warnings.warn("Tablets not yet supported on macOS.")
return []

View File

@ -1,11 +1,13 @@
import sys
import warnings
from ctypes import CFUNCTYPE, byref, c_void_p, c_int, c_ubyte, c_bool, c_uint32, c_uint64
import pyglet.app
from .controller import get_mapping, create_guid
from .base import Device, AbsoluteAxis, RelativeAxis, Button
from .base import Joystick, Controller, AppleRemote, ControllerManager
from pyglet.input.controller import get_mapping, create_guid
from pyglet.input.base import Device, AbsoluteAxis, RelativeAxis, Button
from pyglet.input.base import Joystick, Controller, AppleRemote, ControllerManager
from pyglet.libs.darwin.cocoapy import CFSTR, CFIndex, CFTypeID, known_cftypes
from pyglet.libs.darwin.cocoapy import kCFRunLoopDefaultMode, CFAllocatorRef, cf
@ -248,7 +250,7 @@ class HIDDevice:
_device_lookup[deviceRef.value] = self
self.deviceRef = deviceRef
# Set attributes from device properties.
self.transport = self.get_property("Transport") or "unknown"
self.transport = self.get_property("Transport")
self.vendorID = self.get_property("VendorID")
self.vendorIDSource = self.get_property("VendorIDSource")
self.productID = self.get_property("ProductID")
@ -607,7 +609,8 @@ class PygletDevice(Device):
control.value = hid_value.intvalue
def _create_controls(self):
self._controls = {}
controls = []
for element in self.device.elements:
raw_name = element.name or '0x%x:%x' % (element.usagePage, element.usage)
if element.type in (kIOHIDElementTypeInput_Misc, kIOHIDElementTypeInput_Axis):
@ -623,8 +626,11 @@ class PygletDevice(Device):
continue
control._cookie = element.cookie
control._usage = element.usage
controls.append(control)
self._controls[control._cookie] = control
controls.sort(key=lambda c: c._usage)
self._controls = {control._cookie: control for control in controls}
def _set_initial_control_values(self):
# Must be called AFTER the device has been opened.
@ -685,14 +691,17 @@ def get_apple_remote(display=None):
def _create_controller(device, display):
if device.transport.upper() in ('USB', 'BLUETOOTH'):
if device.transport and device.transport.upper() in ('USB', 'BLUETOOTH'):
mapping = get_mapping(device.get_guid())
if not mapping:
return
if mapping:
return Controller(PygletDevice(display, device, _hid_manager), mapping)
else:
warnings.warn(f"Warning: {device} (GUID: {device.get_guid()}) "
f"has no controller mappings. Update the mappings in the Controller DB.")
def get_controllers(display=None):
return [controller for controller in
[_create_controller(device, display) for device in _hid_manager.devices]
[_create_controller(device, display) for device in _hid_manager.devices
if device.is_joystick() or device.is_gamepad() or device.is_multi_axis()]
if controller is not None]

View File

@ -0,0 +1,109 @@
from typing import Dict, Optional
import pyglet
from pyglet.input import base
from pyglet.input.win32.directinput import DirectInputDevice, _create_controller
from pyglet.input.win32.directinput import _di_manager as _di_device_manager
from pyglet.input.win32.directinput import get_devices as dinput_get_devices
from pyglet.input.win32.directinput import get_controllers as dinput_get_controllers
from pyglet.input.win32.directinput import get_joysticks
try:
from pyglet.input.win32.wintab import get_tablets
except:
def get_tablets(display=None):
import warnings
warnings.warn("Failed to initialize wintab framework.")
return []
_xinput_enabled = False
if not pyglet.options["win32_disable_xinput"]:
try:
from pyglet.input.win32.xinput import XInputControllerManager, XInputController, XInputDevice
from pyglet.input.win32.xinput import _device_manager as _xinput_device_manager
from pyglet.input.win32.xinput import get_devices as xinput_get_devices
from pyglet.input.win32.xinput import get_controllers as xinput_get_controllers
_xinput_enabled = True
except OSError:
# Fail to import XInput.
pass
class Win32ControllerManager(base.ControllerManager):
"""This class manages XInput and DirectInput as a combined manager.
XInput will override any XInput compatible DirectInput devices.
Any devices not supported by XInput will fall back to DirectInput.
"""
def __init__(self):
self._di_controllers: Dict[DirectInputDevice, base.Controller] = {}
if _xinput_enabled:
self._xinput_controllers: Dict[XInputDevice, XInputController] = {}
for xdevice in _xinput_device_manager.all_devices: # All 4 devices are initialized.
meta = {'name': xdevice.name, 'guid': "XINPUTCONTROLLER"}
self._xinput_controllers[xdevice] = XInputController(xdevice, meta)
@_xinput_device_manager.event
def on_connect(xdevice):
self.dispatch_event('on_connect', self._xinput_controllers[xdevice])
@_xinput_device_manager.event
def on_disconnect(xdevice):
self.dispatch_event('on_disconnect', self._xinput_controllers[xdevice])
for device in _di_device_manager.devices:
self._add_di_controller(device)
@_di_device_manager.event
def on_connect(di_device):
if di_device not in self._di_controllers:
if self._add_di_controller(di_device):
pyglet.app.platform_event_loop.post_event(self, 'on_connect', self._di_controllers[di_device])
@_di_device_manager.event
def on_disconnect(di_device):
if di_device in self._di_controllers:
_controller = self._di_controllers[di_device]
del self._di_controllers[di_device]
pyglet.app.platform_event_loop.post_event(self, 'on_disconnect', _controller)
def _add_di_controller(self, device: DirectInputDevice) -> Optional[base.Controller]:
controller = _create_controller(device)
if controller:
self._di_controllers[device] = controller
return controller
return None
def _get_xinput_controllers(self) -> list:
if not _xinput_enabled:
return []
return [ctlr for ctlr in self._xinput_controllers.values() if ctlr.device.connected]
def _get_di_controllers(self) -> list:
return list(self._di_controllers.values())
def get_controllers(self):
return self._get_xinput_controllers() + self._get_di_controllers()
def xinput_get_devices():
return []
def xinput_get_controllers():
return []
def get_devices(display=None):
return xinput_get_devices() + dinput_get_devices(display)
def get_controllers(display=None):
return xinput_get_controllers() + dinput_get_controllers(display)

View File

@ -1,11 +1,21 @@
import ctypes
import warnings
from typing import List, Dict, Optional
from pyglet.libs.win32.constants import WM_DEVICECHANGE, DBT_DEVICEARRIVAL, DBT_DEVICEREMOVECOMPLETE, \
DBT_DEVTYP_DEVICEINTERFACE, DEVICE_NOTIFY_WINDOW_HANDLE
from pyglet.event import EventDispatcher
import pyglet
from pyglet.input import base
from pyglet.libs import win32
from pyglet.libs.win32 import dinput
from pyglet.libs.win32 import dinput, _user32, DEV_BROADCAST_DEVICEINTERFACE, com, DEV_BROADCAST_HDR
from pyglet.libs.win32 import _kernel32
from .controller import get_mapping
from pyglet.input.controller import get_mapping
from pyglet.input.base import ControllerManager
from pyglet.libs.win32.dinput import DIPROPHEADER
# These instance names are not defined anywhere, obtained by experiment. The
# GUID names (which seem to be ideally what are needed) are wrong/missing for
@ -170,15 +180,17 @@ class DirectInputDevice(base.Device):
index = event.dwOfs // 4
self.controls[index].value = event.dwData
def matches(self, guid_id, device_instance):
if (self.id_product_guid == guid_id and
self.id_name == device_instance.contents.tszProductName and
self._type == device_instance.contents.dwDevType & 0xff and
self._subtype == device_instance.contents.dwDevType & 0xff00):
return True
_i_dinput = None
return False
def _init_directinput():
global _i_dinput
if _i_dinput:
return
_i_dinput = dinput.IDirectInput8()
module_handle = _kernel32.GetModuleHandleW(None)
dinput.DirectInput8Create(module_handle,
@ -187,14 +199,151 @@ def _init_directinput():
ctypes.byref(_i_dinput),
None)
return _i_dinput
_i_dinput = _init_directinput()
GUID_DEVINTERFACE_HID = com.GUID(0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30)
class DIManager(EventDispatcher):
def __init__(self):
# Pick any open window, or the shadow window if no windows have been created yet.
window = pyglet.gl._shadow_window
for window in pyglet.app.windows:
break
self.window = window
dbi = DEV_BROADCAST_DEVICEINTERFACE()
dbi.dbcc_size = ctypes.sizeof(dbi)
dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE
dbi.dbcc_classguid = GUID_DEVINTERFACE_HID
# Register we look for HID device unplug/plug.
_user32.RegisterDeviceNotificationW(window._hwnd, ctypes.byref(dbi), DEVICE_NOTIFY_WINDOW_HANDLE)
window._event_handlers[WM_DEVICECHANGE] = self._event_devicechange
# All devices.
self.devices: List[DirectInputDevice] = []
new_devices, _ = self._get_devices()
self.devices.extend(new_devices)
def __del__(self):
del self.window._event_handlers[WM_DEVICECHANGE] # Remove handler.
def _get_devices(self, display=None):
"""Enumerate all the devices on the system.
Returns two values: new devices, missing devices"""
_missing_devices = list(self.devices)
_new_devices = []
_xinput_devices = []
if not pyglet.options["win32_disable_xinput"]:
try:
from pyglet.input.win32.xinput import get_xinput_guids
_xinput_devices = get_xinput_guids()
except ImportError:
pass
def _device_enum(device_instance, arg): # DIDEVICEINSTANCE
guid_id = format(device_instance.contents.guidProduct.Data1, "08x")
# Only XInput should handle XInput compatible devices if enabled. Filter them out.
if guid_id in _xinput_devices:
return dinput.DIENUM_CONTINUE
# Check if device already exists.
for dev in list(_missing_devices):
if dev.matches(guid_id, device_instance):
_missing_devices.remove(dev)
return dinput.DIENUM_CONTINUE
device = dinput.IDirectInputDevice8()
_i_dinput.CreateDevice(device_instance.contents.guidInstance, ctypes.byref(device), None)
di_dev = DirectInputDevice(display, device, device_instance.contents)
_new_devices.append(di_dev)
return dinput.DIENUM_CONTINUE
_i_dinput.EnumDevices(dinput.DI8DEVCLASS_ALL,
dinput.LPDIENUMDEVICESCALLBACK(_device_enum),
None,
dinput.DIEDFL_ATTACHEDONLY)
return _new_devices, _missing_devices
def _recheck_devices(self):
new_devices, missing_devices = self._get_devices()
if new_devices:
self.devices.extend(new_devices)
for device in new_devices:
self.dispatch_event('on_connect', device)
if missing_devices:
for device in missing_devices:
self.devices.remove(device)
self.dispatch_event('on_disconnect', device)
def _event_devicechange(self, msg, wParam, lParam):
if lParam == 0:
return
if wParam == DBT_DEVICEARRIVAL or wParam == DBT_DEVICEREMOVECOMPLETE:
hdr_ptr = ctypes.cast(lParam, ctypes.POINTER(DEV_BROADCAST_HDR))
if hdr_ptr.contents.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE:
self._recheck_devices()
DIManager.register_event_type('on_connect')
DIManager.register_event_type('on_disconnect')
_di_manager = DIManager()
class DIControllerManager(ControllerManager):
def __init__(self, display=None):
self._display = display
self._controllers: Dict[DirectInputDevice, base.Controller] = {}
for device in _di_manager.devices:
self._add_controller(device)
@_di_manager.event
def on_connect(di_device):
if di_device not in self._controllers:
if self._add_controller(di_device):
pyglet.app.platform_event_loop.post_event(self, 'on_connect', self._controllers[di_device])
@_di_manager.event
def on_disconnect(di_device):
if di_device in self._controllers:
_controller = self._controllers[di_device]
del self._controllers[di_device]
pyglet.app.platform_event_loop.post_event(self, 'on_disconnect', _controller)
def _add_controller(self, device: DirectInputDevice) -> Optional[base.Controller]:
controller = _create_controller(device)
if controller:
self._controllers[device] = controller
return controller
return None
def get_controllers(self):
return list(self._controllers.values())
def get_devices(display=None):
_init_directinput()
_devices = []
_xinput_devices = []
if not pyglet.options["win32_disable_xinput"]:
try:
from .xinput import get_xinput_guids
from pyglet.input.win32.xinput import get_xinput_guids
_xinput_devices = get_xinput_guids()
except ImportError:
pass
@ -218,6 +367,16 @@ def get_devices(display=None):
return _devices
def _create_controller(device):
mapping = get_mapping(device.get_guid())
if device._type in (dinput.DI8DEVTYPE_JOYSTICK, dinput.DI8DEVTYPE_1STPERSON, dinput.DI8DEVTYPE_GAMEPAD):
if mapping is not None:
return base.Controller(device, mapping)
else:
warnings.warn(f"Warning: {device} (GUID: {device.get_guid()}) "
f"has no controller mappings. Update the mappings in the Controller DB.")
def _create_joystick(device):
if device._type in (dinput.DI8DEVTYPE_JOYSTICK,
dinput.DI8DEVTYPE_1STPERSON,
@ -228,19 +387,11 @@ def _create_joystick(device):
def get_joysticks(display=None):
return [joystick for joystick in
[_create_joystick(device) for device in get_devices(display)]
[_create_joystick(device) for device in _di_manager.devices]
if joystick is not None]
def _create_controller(device):
mapping = get_mapping(device.get_guid())
if mapping is not None and device._type in (dinput.DI8DEVTYPE_JOYSTICK,
dinput.DI8DEVTYPE_1STPERSON,
dinput.DI8DEVTYPE_GAMEPAD):
return base.Controller(device, mapping)
def get_controllers(display=None):
return [controller for controller in
[_create_controller(device) for device in get_devices(display)]
[_create_controller(device) for device in _di_manager.devices]
if controller is not None]

View File

@ -163,8 +163,8 @@ class XINPUT_CAPABILITIES_EX(Structure):
]
# Only available for 1.4+
if library_name == "xinput1_4":
# Only available for 1.4+
XInputGetBatteryInformation = lib.XInputGetBatteryInformation
XInputGetBatteryInformation.argtypes = [DWORD, BYTE, POINTER(XINPUT_BATTERY_INFORMATION)]
XInputGetBatteryInformation.restype = DWORD
@ -480,6 +480,7 @@ class XInputDeviceManager(EventDispatcher):
# Newly disconnected device:
if device.connected:
device.connected = False
device.close()
self._connected_devices.remove(i)
# Dispatch event in main thread:
pyglet.app.platform_event_loop.post_event(self, 'on_disconnect', device)

View File

@ -104,7 +104,7 @@ class LibraryLoader:
if self.platform.startswith('linux'):
for name in names:
libname = self.find_library(name)
platform_names.append(libname or 'lib%s.so' % name)
platform_names.append(libname or f'lib{name}.so')
platform_names.extend(names)
for name in platform_names:
@ -131,7 +131,7 @@ class LibraryLoader:
if _debug_lib:
print(f"Unexpected error loading library {name}: {str(o)}")
raise ImportError('Library "%s" not found.' % names[0])
raise ImportError(f'Library "{names[0]}" not found.')
def find_library(self, name):
return ctypes.util.find_library(name)
@ -238,7 +238,7 @@ class MachOLibraryLoader(LibraryLoader):
lib = _TraceLibrary(lib)
return lib
raise ImportError("Can't find framework %s." % name)
raise ImportError(f"Can't find framework {name}.")
class LinuxLibraryLoader(LibraryLoader):

View File

@ -366,9 +366,11 @@ objc.property_getName.argtypes = [c_void_p]
objc.protocol_conformsToProtocol.restype = c_bool
objc.protocol_conformsToProtocol.argtypes = [c_void_p, c_void_p]
class OBJC_METHOD_DESCRIPTION(Structure):
_fields_ = [("name", c_void_p), ("types", c_char_p)]
# struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount)
# You must free() the returned array.
objc.protocol_copyMethodDescriptionList.restype = POINTER(OBJC_METHOD_DESCRIPTION)
@ -407,6 +409,7 @@ objc.sel_isEqual.argtypes = [c_void_p, c_void_p]
objc.sel_registerName.restype = c_void_p
objc.sel_registerName.argtypes = [c_char_p]
######################################################################
# Constants
OBJC_ASSOCIATION_ASSIGN = 0 # Weak reference to the associated object.
@ -419,20 +422,25 @@ def ensure_bytes(x):
return x
return x.encode('ascii')
######################################################################
def get_selector(name):
return c_void_p(objc.sel_registerName(ensure_bytes(name)))
def get_class(name):
return c_void_p(objc.objc_getClass(ensure_bytes(name)))
def get_object_class(obj):
return c_void_p(objc.object_getClass(obj))
def get_metaclass(name):
return c_void_p(objc.objc_getMetaClass(ensure_bytes(name)))
def get_superclass_of_object(obj):
cls = c_void_p(objc.object_getClass(obj))
return c_void_p(objc.class_getSuperclass(cls))
@ -451,6 +459,7 @@ def x86_should_use_stret(restype):
return False
return True
# http://www.sealiesoftware.com/blog/archive/2008/11/16/objc_explain_objc_msgSend_fpret.html
def should_use_fpret(restype):
"""Determine if objc_msgSend_fpret is required to return a floating point type."""
@ -464,6 +473,7 @@ def should_use_fpret(restype):
return True
return False
# By default, assumes that restype is c_void_p
# and that all arguments are wrapped inside c_void_p.
# Use the restype and argtypes keyword arguments to
@ -494,11 +504,14 @@ def send_message(receiver, selName, *args, **kwargs):
result = c_void_p(result)
return result
class OBJC_SUPER(Structure):
_fields_ = [('receiver', c_void_p), ('class', c_void_p)]
OBJC_SUPER_PTR = POINTER(OBJC_SUPER)
# http://stackoverflow.com/questions/3095360/what-exactly-is-super-in-objective-c
#
# `superclass_name` is optional and can be used to force finding the superclass
@ -526,10 +539,12 @@ def send_super(receiver, selName, *args, superclass_name=None, **kwargs):
result = c_void_p(result)
return result
######################################################################
cfunctype_table = {}
def parse_type_encoding(encoding):
"""Takes a type encoding string and outputs a list of the separated type codes.
Currently does not handle unions or bitfields and strips out any field width
@ -632,6 +647,7 @@ def cfunctype_for_encoding(encoding):
cfunctype_table[encoding] = cfunctype
return cfunctype
######################################################################
# After calling create_subclass, you must first register
@ -643,9 +659,11 @@ def create_subclass(superclass, name):
superclass = get_class(superclass)
return c_void_p(objc.objc_allocateClassPair(superclass, ensure_bytes(name), 0))
def register_subclass(subclass):
objc.objc_registerClassPair(subclass)
# types is a string encoding the argument types of the method.
# The first type code of types is the return type (e.g. 'v' if void)
# The second type code must be '@' for id self.
@ -662,18 +680,22 @@ def add_method(cls, selName, method, types):
objc.class_addMethod(cls, selector, imp, types)
return imp
def add_ivar(cls, name, vartype):
return objc.class_addIvar(cls, ensure_bytes(name), sizeof(vartype), alignment(vartype), encoding_for_ctype(vartype))
def set_instance_variable(obj, varname, value, vartype):
objc.object_setInstanceVariable.argtypes = [c_void_p, c_char_p, vartype]
objc.object_setInstanceVariable(obj, ensure_bytes(varname), value)
def get_instance_variable(obj, varname, vartype):
variable = vartype()
objc.object_getInstanceVariable(obj, ensure_bytes(varname), byref(variable))
return variable.value
######################################################################
class ObjCMethod:
@ -712,7 +734,7 @@ class ObjCMethod:
try:
self.argtypes = [self.ctype_for_encoding(t) for t in self.argument_types]
except:
#print('no argtypes encoding for %s (%s)' % (self.name, self.argument_types))
# print(f'no argtypes encoding for {self.name} ({self.argument_types})')
self.argtypes = None
# Get types for the return type.
try:
@ -723,7 +745,7 @@ class ObjCMethod:
else:
self.restype = self.ctype_for_encoding(self.return_type)
except:
#print('no restype encoding for %s (%s)' % (self.name, self.return_type))
# print(f'no restype encoding for {self.name} ({self.return_type})')
self.restype = None
self.func = None
@ -792,6 +814,7 @@ class ObjCMethod:
'encoding = ' + str(self.encoding))
raise
######################################################################
class ObjCBoundMethod:
@ -817,6 +840,7 @@ class ObjCBoundMethod:
"""Call the method with the given arguments."""
return self.method(self.objc_id, *args)
######################################################################
class ObjCClass:
@ -940,6 +964,7 @@ class ObjCClass:
# Otherwise, raise an exception.
raise AttributeError('ObjCClass %s has no attribute %s' % (self.name, name))
######################################################################
class ObjCInstance:
@ -1043,6 +1068,7 @@ def convert_method_arguments(encoding, args):
new_args.append(a)
return new_args
# ObjCSubclass is used to define an Objective-C subclass of an existing
# class registered with the runtime. When you create an instance of
# ObjCSubclass, it registers the new subclass with the Objective-C
@ -1139,10 +1165,12 @@ class ObjCSubclass:
typecodes = parse_type_encoding(encoding)
typecodes.insert(1, b'@:')
encoding = b''.join(typecodes)
def decorator(f):
name = f.__name__.replace('_', ':')
self.add_method(f, name, encoding)
return f
return decorator
def method(self, encoding):
@ -1152,6 +1180,7 @@ class ObjCSubclass:
typecodes = parse_type_encoding(encoding)
typecodes.insert(1, b'@:')
encoding = b''.join(typecodes)
def decorator(f):
def objc_method(objc_self, objc_cmd, *args):
py_self = ObjCInstance(objc_self)
@ -1163,9 +1192,11 @@ class ObjCSubclass:
elif isinstance(result, ObjCInstance):
result = result.ptr.value
return result
name = f.__name__.replace('_', ':')
self.add_method(objc_method, name, encoding)
return objc_method
return decorator
def classmethod(self, encoding):
@ -1175,6 +1206,7 @@ class ObjCSubclass:
typecodes = parse_type_encoding(encoding)
typecodes.insert(1, b'@:')
encoding = b''.join(typecodes)
def decorator(f):
def objc_class_method(objc_cls, objc_cmd, *args):
py_cls = ObjCClass(objc_cls)
@ -1186,11 +1218,14 @@ class ObjCSubclass:
elif isinstance(result, ObjCInstance):
result = result.ptr.value
return result
name = f.__name__.replace('_', ':')
self.add_class_method(objc_class_method, name, encoding)
return objc_class_method
return decorator
######################################################################
# Instances of DeallocationObserver are associated with every

View File

@ -207,6 +207,8 @@ _user32.GetRawInputData.restype = UINT
_user32.GetRawInputData.argtypes = [HRAWINPUT, UINT, LPVOID, PUINT, UINT]
_user32.ChangeWindowMessageFilterEx.restype = BOOL
_user32.ChangeWindowMessageFilterEx.argtypes = [HWND, UINT, DWORD, c_void_p]
_user32.RegisterDeviceNotificationW.restype = LPVOID
_user32.RegisterDeviceNotificationW.argtypes = [HANDLE, LPVOID, DWORD]
# dwmapi
_dwmapi.DwmIsCompositionEnabled.restype = c_int

View File

@ -5069,3 +5069,11 @@ STREAM_SEEK_CUR = 1
STREAM_SEEK_END = 2
LOCALE_NAME_MAX_LENGTH = 85
DBT_DEVICEARRIVAL = 0x8000
DBT_DEVICEREMOVECOMPLETE = 0x8004
DBT_DEVTYP_DEVICEINTERFACE = 5
DEVICE_NOTIFY_WINDOW_HANDLE = 0
DEVICE_NOTIFY_SERVICE_HANDLE = 1

View File

@ -208,6 +208,7 @@ DISCL_BACKGROUND = 0x00000008
DISCL_NOWINKEY = 0x00000010
DIPROP_BUFFERSIZE = 1
DIPROP_GUIDANDPATH = com.GUID(12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
GUID_XAxis = \
com.GUID(0xA36D02E0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00)
@ -310,7 +311,7 @@ class IDirectInputDevice8(com.pIUnknown):
('EnumObjects',
com.STDMETHOD(LPDIENUMDEVICEOBJECTSCALLBACK, LPVOID, DWORD)),
('GetProperty',
com.STDMETHOD()),
com.STDMETHOD(LPVOID, LPDIPROPHEADER)),
('SetProperty',
com.STDMETHOD(LPVOID, LPDIPROPHEADER)),
('Acquire',

View File

@ -570,3 +570,19 @@ class IStream(com.pIUnknown):
('Clone',
com.STDMETHOD()),
]
class DEV_BROADCAST_HDR(Structure):
_fields_ = (
('dbch_size', DWORD),
('dbch_devicetype', DWORD),
('dbch_reserved', DWORD),
)
class DEV_BROADCAST_DEVICEINTERFACE(Structure):
_fields_ = (
('dbcc_size', DWORD),
('dbcc_devicetype', DWORD),
('dbcc_reserved', DWORD),
('dbcc_classguid', com.GUID),
('dbcc_name', ctypes.c_wchar * 256)
)

View File

@ -53,7 +53,7 @@ def get_audio_driver():
break
except Exception:
if _debug:
print('Error importing driver %s:' % driver_name)
print(f'Error importing driver {driver_name}:')
import traceback
traceback.print_exc()
else:

View File

@ -175,7 +175,7 @@ class PulseAudioPlayer(AbstractAudioPlayer):
def _write_to_stream(self, nbytes=None):
if nbytes is None:
nbytes = self.stream.writable_size
assert _debug('PulseAudioPlayer: Requested to write %d bytes to stream' % nbytes)
assert _debug(f'PulseAudioPlayer: Requested to write {nbytes} bytes to stream')
seek_mode = pa.PA_SEEK_RELATIVE
if self._clear_write:

View File

@ -66,7 +66,7 @@ class MediaThread:
Time to wait, in seconds.
"""
assert _debug('MediaThread.sleep(%r)' % timeout)
assert _debug(f'MediaThread.sleep({timeout!r})')
with self._condition:
if not self._stopped:
self._condition.wait(timeout)

View File

@ -149,16 +149,16 @@ def get_settings_path(name):
if 'APPDATA' in os.environ:
return os.path.join(os.environ['APPDATA'], name)
else:
return os.path.expanduser('~/%s' % name)
return os.path.expanduser(f'~/{name}')
elif pyglet.compat_platform == 'darwin':
return os.path.expanduser('~/Library/Application Support/%s' % name)
return os.path.expanduser(f'~/Library/Application Support/{name}')
elif pyglet.compat_platform.startswith('linux'):
if 'XDG_CONFIG_HOME' in os.environ:
return os.path.join(os.environ['XDG_CONFIG_HOME'], name)
else:
return os.path.expanduser('~/.config/%s' % name)
return os.path.expanduser(f'~/.config/{name}')
else:
return os.path.expanduser('~/.%s' % name)
return os.path.expanduser(f'~/.{name}')
def get_data_path(name):
@ -190,16 +190,16 @@ def get_data_path(name):
if 'APPDATA' in os.environ:
return os.path.join(os.environ['APPDATA'], name)
else:
return os.path.expanduser('~/%s' % name)
return os.path.expanduser(f'~/{name}')
elif pyglet.compat_platform == 'darwin':
return os.path.expanduser('~/Library/Application Support/%s' % name)
return os.path.expanduser(f'~/Library/Application Support/{name}')
elif pyglet.compat_platform.startswith('linux'):
if 'XDG_DATA_HOME' in os.environ:
return os.path.join(os.environ['XDG_DATA_HOME'], name)
else:
return os.path.expanduser('~/.local/share/%s' % name)
return os.path.expanduser(f'~/.local/share/{name}')
else:
return os.path.expanduser('~/.%s' % name)
return os.path.expanduser(f'~/.{name}')
class Location:

View File

@ -113,7 +113,7 @@ def get_decoder(filename, mimetype=None):
from pyglet.text.formats import attributed
return attributed.AttributedTextDecoder()
else:
raise DocumentDecodeException('Unknown format "%s"' % mimetype)
raise DocumentDecodeException(f'Unknown format "{mimetype}"')
def load(filename, file=None, mimetype=None):

View File

@ -452,7 +452,7 @@ class AbstractDocument(event.EventDispatcher):
for element in self._elements:
if element._position == position:
return element
raise RuntimeError('No element at position %d' % position)
raise RuntimeError(f'No element at position {position}')
def set_style(self, start, end, attributes):
"""Set text style of some or all of the document.

View File

@ -201,7 +201,7 @@ class OrderedListBuilder(ListBuilder):
mark = '?'
if self.numbering == 'A':
mark = mark.upper()
return '%s%s%s' % (self.prefix, mark, self.suffix)
return f'{self.prefix}{mark}{self.suffix}'
elif self.numbering in 'iI':
try:
mark = _int_to_roman(value)
@ -209,9 +209,9 @@ class OrderedListBuilder(ListBuilder):
mark = '?'
if self.numbering == 'i':
mark = mark.lower()
return '%s%s%s' % (self.prefix, mark, self.suffix)
return f'{self.prefix}{mark}{self.suffix}'
else:
return '%s%d%s' % (self.prefix, value, self.suffix)
return f'{self.prefix}{value}{self.suffix}'
class StructuredTextDecoder(pyglet.text.DocumentDecoder):

View File

@ -145,7 +145,7 @@ def _parse_distance(distance, dpi):
return int(distance)
match = _distance_re.match(distance)
assert match, 'Could not parse distance %s' % distance
assert match, f'Could not parse distance {distance}'
if not match:
return 0
@ -164,7 +164,7 @@ def _parse_distance(distance, dpi):
elif unit == 'cm':
return int(value * dpi * 0.393700787)
else:
assert False, 'Unknown distance unit %s' % unit
assert False, f'Unknown distance unit {unit}'
class _Line:
@ -190,7 +190,7 @@ class _Line:
self.boxes = []
def __repr__(self):
return '_Line(%r)' % self.boxes
return f'_Line({self.boxes})'
def add_box(self, box):
self.boxes.append(box)
@ -425,7 +425,7 @@ class _GlyphBox(_AbstractBox):
return position
def __repr__(self):
return '_GlyphBox(%r)' % self.glyphs
return f'_GlyphBox({self.glyphs})'
class _InlineElementBox(_AbstractBox):
@ -460,7 +460,7 @@ class _InlineElementBox(_AbstractBox):
return 1
def __repr__(self):
return '_InlineElementBox(%r)' % self.element
return f'_InlineElementBox({self.element})'
class _InvalidRange:

View File

@ -10,7 +10,7 @@ class _Run:
self.count = count
def __repr__(self):
return 'Run(%r, %d)' % (self.value, self.count)
return f'Run({self.value}, {self.count})'
class RunList:

View File

@ -607,7 +607,7 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
pass
def __repr__(self):
return '%s(width=%d, height=%d)' % (self.__class__.__name__, self.width, self.height)
return f'{self.__class__.__name__}=(width={self.width}, height={self.height})'
def _create(self):
raise NotImplementedError('abstract')
@ -734,7 +734,7 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
self.screen.set_mode(mode)
elif self.screen.get_modes():
# Only raise exception if mode switching is at all possible.
raise NoSuchScreenModeException('No mode matching %dx%d' % (width, height))
raise NoSuchScreenModeException(f'No mode matching {width}x{height}')
else:
width = self.screen.width
height = self.screen.height