sync pyglet and lib-not-dr

This commit is contained in:
shenjack 2023-11-03 16:24:59 +08:00
parent 8857166e67
commit b28ef5c580
Signed by: shenjack
GPG Key ID: 7B1134A979775551
12 changed files with 338 additions and 151 deletions

View File

@ -9,26 +9,11 @@ import inspect
from types import FrameType from types import FrameType
from typing import List, Optional from typing import List, Optional
from .structers import LogMessage from lib_not_dr.logger.structers import LogMessage
from lib_not_dr.logger.outstream import BaseOutputStream
from lib_not_dr.types.options import Options from lib_not_dr.types.options import Options
class BaseOutputStream(Options):
name = 'BaseOutputStream'
level: int = 20
enable: bool = True
def write_stdout(self, message: LogMessage) -> None:
raise NotImplementedError(f'{self.__class__.__name__}.write_stdout is not implemented')
def write_stderr(self, message: LogMessage) -> None:
raise NotImplementedError(f'{self.__class__.__name__}.write_stderr is not implemented')
def flush(self) -> None:
raise NotImplementedError(f'{self.__class__.__name__}.flush is not implemented')
class Logger(Options): class Logger(Options):
name = 'Logger-v2' name = 'Logger-v2'

View File

@ -0,0 +1,62 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
import sys
from lib_not_dr.types.options import Options
from lib_not_dr.logger.structers import LogMessage
from lib_not_dr.logger.formatter import BaseFormatter, StdFormatter
__all__ = [
'BaseOutputStream'
]
class BaseOutputStream(Options):
name = 'BaseOutputStream'
level: int = 20
enable: bool = True
def write_stdout(self, message: LogMessage) -> None:
raise NotImplementedError(f'{self.__class__.__name__}.write_stdout is not implemented')
def write_stderr(self, message: LogMessage) -> None:
raise NotImplementedError(f'{self.__class__.__name__}.write_stderr is not implemented')
def flush(self) -> None:
raise NotImplementedError(f'{self.__class__.__name__}.flush is not implemented')
class StdioOutputStream(BaseOutputStream):
name = 'StdioOutputStream'
formatter: BaseFormatter = StdFormatter()
def write_stdout(self, message: LogMessage) -> None:
if not self.enable:
return None
if message.level < self.level:
return None
print(self.formatter.format_message(message), end='', flush=message.flush)
return None
def write_stderr(self, message: LogMessage) -> None:
if not self.enable:
return None
if message.level < self.level:
return None
print(self.formatter.format_message(message), end='', flush=message.flush, file=sys.stderr)
return None
def flush(self) -> None:
"""
flush stdout and stderr
:return: None
"""
print('', end='', flush=True)
print('', end='', flush=True, file=sys.stderr)
return None

View File

@ -1,6 +1,7 @@
import weakref import weakref
from enum import Enum from enum import Enum
import threading
from typing import Tuple from typing import Tuple
import pyglet import pyglet
@ -209,11 +210,12 @@ class CanvasConfig(Config):
class ObjectSpace: class ObjectSpace:
def __init__(self): def __init__(self):
# Textures and buffers scheduled for deletion # Objects scheduled for deletion the next time this object space is active.
# the next time this object space is active.
self.doomed_textures = [] self.doomed_textures = []
self.doomed_buffers = [] self.doomed_buffers = []
self.doomed_shader_programs = [] self.doomed_shader_programs = []
self.doomed_shaders = []
self.doomed_renderbuffers = []
class Context: class Context:
@ -236,6 +238,7 @@ class Context:
self.canvas = None self.canvas = None
self.doomed_vaos = [] self.doomed_vaos = []
self.doomed_framebuffers = []
if context_share: if context_share:
self.object_space = context_share.object_space self.object_space = context_share.object_space
@ -277,28 +280,44 @@ class Context:
self._info = gl_info.GLInfo() self._info = gl_info.GLInfo()
self._info.set_active_context() self._info.set_active_context()
# Release Textures, Buffers, and VAOs on this context scheduled for
# deletion. Note that the garbage collector may introduce a race
# condition, so operate on a copy, and clear the list afterward.
if self.object_space.doomed_textures: if self.object_space.doomed_textures:
textures = self.object_space.doomed_textures[:] self._delete_objects(self.object_space.doomed_textures, gl.glDeleteTextures)
textures = (gl.GLuint * len(textures))(*textures)
gl.glDeleteTextures(len(textures), textures)
self.object_space.doomed_textures.clear()
if self.object_space.doomed_buffers: if self.object_space.doomed_buffers:
buffers = self.object_space.doomed_buffers[:] self._delete_objects(self.object_space.doomed_buffers, gl.glDeleteBuffers)
buffers = (gl.GLuint * len(buffers))(*buffers)
gl.glDeleteBuffers(len(buffers), buffers)
self.object_space.doomed_buffers.clear()
if self.object_space.doomed_shader_programs: if self.object_space.doomed_shader_programs:
for program_id in self.object_space.doomed_shader_programs: self._delete_objects_one_by_one(self.object_space.doomed_shader_programs,
gl.glDeleteProgram(program_id) gl.glDeleteProgram)
self.object_space.doomed_shader_programs.clear() if self.object_space.doomed_shaders:
self._delete_objects_one_by_one(self.object_space.doomed_shaders, gl.glDeleteShader)
if self.object_space.doomed_renderbuffers:
self._delete_objects(self.object_space.doomed_renderbuffers, gl.glDeleteRenderbuffers)
if self.doomed_vaos: if self.doomed_vaos:
vaos = self.doomed_vaos[:] self._delete_objects(self.doomed_vaos, gl.glDeleteVertexArrays)
vaos = (gl.GLuint * len(vaos))(*vaos) if self.doomed_framebuffers:
gl.glDeleteVertexArrays(len(vaos), vaos) self._delete_objects(self.doomed_framebuffers, gl.glDeleteFramebuffers)
self.doomed_vaos.clear()
# For the functions below:
# The garbage collector introduces a race condition.
# The provided list might be appended to (and only appended to) while this
# method runs, as it's a `doomed_*` list either on the context or
# its object space. This is why we drain it relying on `pop`s atomicity.
def _delete_objects(self, list_, deletion_func):
"""Release all OpenGL objects in the given list using the supplied
deletion function with the signature ``(GLuint count, GLuint *names)``.
"""
to_delete = []
while list_:
to_delete.append(list_.pop())
deletion_func(len(to_delete), (gl.GLuint * len(to_delete))(*to_delete))
def _delete_objects_one_by_one(self, list_, deletion_func):
"""Similar to ``_delete_objects``, but assumes the deletion functions's
signature to be ``(GLuint name)``, calling it once for each object.
"""
while list_:
deletion_func(gl.GLuint(list_.pop()))
def destroy(self): def destroy(self):
"""Release the context. """Release the context.
@ -318,6 +337,27 @@ class Context:
if gl._shadow_window is not None: if gl._shadow_window is not None:
gl._shadow_window.switch_to() gl._shadow_window.switch_to()
def _safe_to_operate_on_object_space(self):
"""Return whether it is safe to interact with this context's object
space.
This is considered to be the case if the currently active context's
object space is the same as this context's object space and this
method is called from the main thread.
"""
return (
self.object_space is gl.current_context.object_space and
threading.current_thread() is threading.main_thread()
)
def _safe_to_operate_on(self):
"""Return whether it is safe to interact with this context.
This is considered to be the case if it's the current context and this
method is called from the main thread.
"""
return gl.current_context is self and threading.current_thread() is threading.main_thread()
def create_program(self, *sources: Tuple[str, str], program_class=None): def create_program(self, *sources: Tuple[str, str], program_class=None):
"""Create a ShaderProgram from OpenGL GLSL source. """Create a ShaderProgram from OpenGL GLSL source.
@ -347,25 +387,30 @@ class Context:
return program return program
def delete_texture(self, texture_id): def delete_texture(self, texture_id):
"""Safely delete a Texture belonging to this context. """Safely delete a Texture belonging to this context's object space.
Usually, the Texture is released immediately using This method will delete the texture immediately via
``glDeleteTextures``, however if another context that does not share ``glDeleteTextures`` if the current context's object space is the same
this context's object space is currently active, the deletion will as this context's object space and it is called from the main thread.
be deferred until an appropriate context is activated.
Otherwise, the texture will only be marked for deletion, postponing
it until any context with the same object space becomes active again.
This makes it safe to call from anywhere, including other threads.
:Parameters: :Parameters:
`texture_id` : int `texture_id` : int
The OpenGL name of the Texture to delete. The OpenGL name of the Texture to delete.
""" """
if self.object_space is gl.current_context.object_space: if self._safe_to_operate_on_object_space():
gl.glDeleteTextures(1, gl.GLuint(texture_id)) gl.glDeleteTextures(1, gl.GLuint(texture_id))
else: else:
self.object_space.doomed_textures.append(texture_id) self.object_space.doomed_textures.append(texture_id)
def delete_buffer(self, buffer_id): def delete_buffer(self, buffer_id):
"""Safely delete a Buffer object belonging to this context. """Safely delete a Buffer object belonging to this context's object
space.
This method behaves similarly to `delete_texture`, though for This method behaves similarly to `delete_texture`, though for
``glDeleteBuffers`` instead of ``glDeleteTextures``. ``glDeleteBuffers`` instead of ``glDeleteTextures``.
@ -376,30 +421,14 @@ class Context:
.. versionadded:: 1.1 .. versionadded:: 1.1
""" """
if self.object_space is gl.current_context.object_space and False: if self._safe_to_operate_on_object_space():
gl.glDeleteBuffers(1, gl.GLuint(buffer_id)) gl.glDeleteBuffers(1, gl.GLuint(buffer_id))
else: else:
self.object_space.doomed_buffers.append(buffer_id) self.object_space.doomed_buffers.append(buffer_id)
def delete_vao(self, vao_id):
"""Safely delete a Vertex Array Object belonging to this context.
This method behaves similarly to `delete_texture`, though for
``glDeleteVertexArrays`` instead of ``glDeleteTextures``.
:Parameters:
`vao_id` : int
The OpenGL name of the Vertex Array to delete.
.. versionadded:: 2.0
"""
if gl.current_context is self:
gl.glDeleteVertexArrays(1, gl.GLuint(vao_id))
else:
self.doomed_vaos.append(vao_id)
def delete_shader_program(self, program_id): def delete_shader_program(self, program_id):
"""Safely delete a Shader Program belonging to this context. """Safely delete a Shader Program belonging to this context's
object space.
This method behaves similarly to `delete_texture`, though for This method behaves similarly to `delete_texture`, though for
``glDeleteProgram`` instead of ``glDeleteTextures``. ``glDeleteProgram`` instead of ``glDeleteTextures``.
@ -410,11 +439,84 @@ class Context:
.. versionadded:: 2.0 .. versionadded:: 2.0
""" """
if gl.current_context is self: if self._safe_to_operate_on_object_space():
gl.glDeleteProgram(program_id) gl.glDeleteProgram(gl.GLuint(program_id))
else: else:
self.object_space.doomed_shader_programs.append(program_id) self.object_space.doomed_shader_programs.append(program_id)
def delete_shader(self, shader_id):
"""Safely delete a Shader belonging to this context's object space.
This method behaves similarly to `delete_texture`, though for
``glDeleteShader`` instead of ``glDeleteTextures``.
:Parameters:
`shader_id` : int
The OpenGL name of the Shader to delete.
.. versionadded:: 2.0.10
"""
if self._safe_to_operate_on_object_space():
gl.glDeleteShader(gl.GLuint(shader_id))
else:
self.object_space.doomed_shaders.append(shader_id)
def delete_renderbuffer(self, rbo_id):
"""Safely delete a Renderbuffer Object belonging to this context's
object space.
This method behaves similarly to `delete_texture`, though for
``glDeleteRenderbuffers`` instead of ``glDeleteTextures``.
:Parameters:
`rbo_id` : int
The OpenGL name of the Shader Program to delete.
.. versionadded:: 2.0.10
"""
if self._safe_to_operate_on_object_space():
gl.glDeleteRenderbuffers(1, gl.GLuint(rbo_id))
else:
self.object_space.doomed_renderbuffers.append(rbo_id)
def delete_vao(self, vao_id):
"""Safely delete a Vertex Array Object belonging to this context.
If this context is not the current context or this method is not
called from the main thread, its deletion will be postponed until
this context is next made active again.
Otherwise, this method will immediately delete the VAO via
``glDeleteVertexArrays``.
:Parameters:
`vao_id` : int
The OpenGL name of the Vertex Array to delete.
.. versionadded:: 2.0
"""
if self._safe_to_operate_on():
gl.glDeleteVertexArrays(1, gl.GLuint(vao_id))
else:
self.doomed_vaos.append(vao_id)
def delete_framebuffer(self, fbo_id):
"""Safely delete a Framebuffer Object belonging to this context.
This method behaves similarly to `delete_vao`, though for
``glDeleteFramebuffers`` instead of ``glDeleteVertexArrays``.
:Parameters:
`fbo_id` : int
The OpenGL name of the Framebuffer Object to delete.
.. versionadded:: 2.0.10
"""
if self._safe_to_operate_on():
gl.glDeleteFramebuffers(1, gl.GLuint(fbo_id))
else:
self.doomed_framebuffers.append(fbo_id)
def get_info(self): def get_info(self):
"""Get the OpenGL information for this context. """Get the OpenGL information for this context.

View File

@ -62,8 +62,7 @@ def errcheck(result, func, arguments):
print(name) print(name)
from pyglet import gl from pyglet import gl
context = gl.current_context if not gl.current_context:
if not context:
raise GLException('No GL context; create a Window first') raise GLException('No GL context; create a Window first')
error = gl.glGetError() error = gl.glGetError()
if error: if error:

View File

@ -674,6 +674,7 @@ class Shader:
""" """
def __init__(self, source_string: str, shader_type: str): def __init__(self, source_string: str, shader_type: str):
self._context = pyglet.gl.current_context
self._id = None self._id = None
self.type = shader_type self.type = shader_type
@ -689,6 +690,7 @@ class Shader:
source_length = c_int(len(shader_source_utf8)) source_length = c_int(len(shader_source_utf8))
shader_id = glCreateShader(shader_type) shader_id = glCreateShader(shader_type)
self._id = shader_id
glShaderSource(shader_id, 1, byref(source_buffer_pointer), source_length) glShaderSource(shader_id, 1, byref(source_buffer_pointer), source_length)
glCompileShader(shader_id) glCompileShader(shader_id)
@ -709,8 +711,6 @@ class Shader:
elif _debug_gl_shaders: elif _debug_gl_shaders:
print(self._get_shader_log(shader_id)) print(self._get_shader_log(shader_id))
self._id = shader_id
@property @property
def id(self): def id(self):
return self._id return self._id
@ -735,16 +735,19 @@ class Shader:
glGetShaderSource(shader_id, source_length, None, source_str) glGetShaderSource(shader_id, source_length, None, source_str)
return source_str.value.decode('utf8') return source_str.value.decode('utf8')
def __del__(self): def delete(self):
try: glDeleteShader(self._id)
glDeleteShader(self._id) self._id = None
if _debug_gl_shaders:
print(f"Destroyed {self.type} Shader '{self._id}'")
except Exception: def __del__(self):
# Interpreter is shutting down, if self._id is not None:
# or Shader failed to compile. try:
pass self._context.delete_shader(self._id)
if _debug_gl_shaders:
print(f"Destroyed {self.type} Shader '{self._id}'")
self._id = None
except (AttributeError, ImportError):
pass # Interpreter is shutting down
def __repr__(self): def __repr__(self):
return "{0}(id={1}, type={2})".format(self.__class__.__name__, self.id, self.type) return "{0}(id={1}, type={2})".format(self.__class__.__name__, self.id, self.type)
@ -756,6 +759,8 @@ class ShaderProgram:
__slots__ = '_id', '_context', '_attributes', '_uniforms', '_uniform_blocks', '__weakref__' __slots__ = '_id', '_context', '_attributes', '_uniforms', '_uniform_blocks', '__weakref__'
def __init__(self, *shaders: Shader): def __init__(self, *shaders: Shader):
self._id = None
assert shaders, "At least one Shader object is required." assert shaders, "At least one Shader object is required."
self._id = _link_program(*shaders) self._id = _link_program(*shaders)
self._context = pyglet.gl.current_context self._context = pyglet.gl.current_context
@ -799,13 +804,17 @@ class ShaderProgram:
def __exit__(self, *_): def __exit__(self, *_):
glUseProgram(0) glUseProgram(0)
def delete(self):
glDeleteProgram(self._id)
self._id = None
def __del__(self): def __del__(self):
try: if self._id is not None:
self._context.delete_shader_program(self.id) try:
except Exception: self._context.delete_shader_program(self._id)
# Interpreter is shutting down, self._id = None
# or ShaderProgram failed to link. except (AttributeError, ImportError):
pass pass # Interpreter is shutting down
def __setitem__(self, key, value): def __setitem__(self, key, value):
try: try:
@ -930,6 +939,8 @@ class ComputeShaderProgram:
def __init__(self, source: str): def __init__(self, source: str):
"""Create an OpenGL ComputeShaderProgram from source.""" """Create an OpenGL ComputeShaderProgram from source."""
self._id = None
if not (gl_info.have_version(4, 3) or gl_info.have_extension("GL_ARB_compute_shader")): if not (gl_info.have_version(4, 3) or gl_info.have_extension("GL_ARB_compute_shader")):
raise ShaderException("Compute Shader not supported. OpenGL Context version must be at least " raise ShaderException("Compute Shader not supported. OpenGL Context version must be at least "
"4.3 or higher, or 4.2 with the 'GL_ARB_compute_shader' extension.") "4.3 or higher, or 4.2 with the 'GL_ARB_compute_shader' extension.")
@ -1002,13 +1013,17 @@ class ComputeShaderProgram:
def __exit__(self, *_): def __exit__(self, *_):
glUseProgram(0) glUseProgram(0)
def delete(self):
glDeleteProgram(self._id)
self._id = None
def __del__(self): def __del__(self):
try: if self._id is not None:
self._context.delete_shader_program(self.id) try:
except Exception: self._context.delete_shader_program(self._id)
# Interpreter is shutting down, self._id = None
# or ShaderProgram failed to link. except (AttributeError, ImportError):
pass pass # Interpreter is shutting down
def __setitem__(self, key, value): def __setitem__(self, key, value):
try: try:

View File

@ -27,10 +27,8 @@ class VertexArray:
glBindVertexArray(0) glBindVertexArray(0)
def delete(self): def delete(self):
try: glDeleteVertexArrays(1, self._id)
glDeleteVertexArrays(1, self._id) self._id = None
except Exception:
pass
__enter__ = bind __enter__ = bind
@ -38,11 +36,12 @@ class VertexArray:
glBindVertexArray(0) glBindVertexArray(0)
def __del__(self): def __del__(self):
try: if self._id is not None:
self._context.delete_vao(self.id) try:
# Python interpreter is shutting down: self._context.delete_vao(self.id)
except ImportError: self._id = None
pass except (ImportError, AttributeError):
pass # Interpreter is shutting down
def __repr__(self): def __repr__(self):
return "{}(id={})".format(self.__class__.__name__, self._id.value) return "{}(id={})".format(self.__class__.__name__, self._id.value)

View File

@ -164,21 +164,18 @@ class BufferObject(AbstractBuffer):
def unmap(self): def unmap(self):
glUnmapBuffer(GL_ARRAY_BUFFER) glUnmapBuffer(GL_ARRAY_BUFFER)
def __del__(self):
try:
if self.id is not None:
self._context.delete_buffer(self.id)
except:
pass
def delete(self): def delete(self):
buffer_id = GLuint(self.id) glDeleteBuffers(1, self.id)
try:
glDeleteBuffers(1, buffer_id)
except Exception:
pass
self.id = None self.id = None
def __del__(self):
if self.id is not None:
try:
self._context.delete_buffer(self.id)
self.id = None
except (AttributeError, ImportError):
pass # Interpreter is shutting down
def resize(self, size): def resize(self, size):
# Map, create a copy, then reinitialize. # Map, create a copy, then reinitialize.
temp = (ctypes.c_byte * size)() temp = (ctypes.c_byte * size)()

View File

@ -1220,11 +1220,20 @@ class Texture(AbstractImage):
self.id = tex_id self.id = tex_id
self._context = pyglet.gl.current_context self._context = pyglet.gl.current_context
def delete(self):
"""Delete this texture and the memory it occupies.
After this, it may not be used anymore.
"""
glDeleteTextures(1, self.id)
self.id = None
def __del__(self): def __del__(self):
try: if self.id is not None:
self._context.delete_texture(self.id) try:
except Exception: self._context.delete_texture(self.id)
pass self.id = None
except (AttributeError, ImportError):
pass # Interpreter is shutting down
def bind(self, texture_unit: int = 0): def bind(self, texture_unit: int = 0):
"""Bind to a specific Texture Unit by number.""" """Bind to a specific Texture Unit by number."""
@ -1479,8 +1488,13 @@ class TextureRegion(Texture):
return "{}(id={}, size={}x{}, owner={}x{})".format(self.__class__.__name__, self.id, self.width, self.height, return "{}(id={}, size={}x{}, owner={}x{})".format(self.__class__.__name__, self.id, self.width, self.height,
self.owner.width, self.owner.height) self.owner.width, self.owner.height)
def delete(self):
"""Deleting a TextureRegion has no effect. Operate on the owning
texture instead.
"""
pass
def __del__(self): def __del__(self):
# only the owner Texture should handle deletion
pass pass

View File

@ -13,6 +13,7 @@ class Renderbuffer:
def __init__(self, width, height, internal_format, samples=1): def __init__(self, width, height, internal_format, samples=1):
"""Create an instance of a Renderbuffer object.""" """Create an instance of a Renderbuffer object."""
self._context = pyglet.gl.current_context
self._id = GLuint() self._id = GLuint()
self._width = width self._width = width
self._height = height self._height = height
@ -49,13 +50,15 @@ class Renderbuffer:
def delete(self): def delete(self):
glDeleteRenderbuffers(1, self._id) glDeleteRenderbuffers(1, self._id)
self._id = None
def __del__(self): def __del__(self):
try: if self._id is not None:
glDeleteRenderbuffers(1, self._id) try:
# Python interpreter is shutting down: self._context.delete_renderbuffer(self._id.value)
except Exception: self._id = None
pass except (AttributeError, ImportError):
pass # Interpreter is shutting down
def __repr__(self): def __repr__(self):
return "{}(id={})".format(self.__class__.__name__, self._id.value) return "{}(id={})".format(self.__class__.__name__, self._id.value)
@ -71,6 +74,7 @@ class Framebuffer:
.. versionadded:: 2.0 .. versionadded:: 2.0
""" """
self._context = pyglet.gl.current_context
self._id = GLuint() self._id = GLuint()
glGenFramebuffers(1, self._id) glGenFramebuffers(1, self._id)
self._attachment_types = 0 self._attachment_types = 0
@ -105,10 +109,16 @@ class Framebuffer:
self.unbind() self.unbind()
def delete(self): def delete(self):
try: glDeleteFramebuffers(1, self._id)
glDeleteFramebuffers(1, self._id) self._id = None
except Exception:
pass def __del__(self):
if self._id is not None:
try:
self._context.delete_framebuffer(self._id.value)
self._id = None
except (AttributeError, ImportError):
pass # Interpreter is shutting down
@property @property
def is_complete(self): def is_complete(self):
@ -203,12 +213,5 @@ class Framebuffer:
self._height = max(renderbuffer.height, self._height) self._height = max(renderbuffer.height, self._height)
self.unbind() self.unbind()
def __del__(self):
try:
glDeleteFramebuffers(1, self._id)
# Python interpreter is shutting down:
except Exception:
pass
def __repr__(self): def __repr__(self):
return "{}(id={})".format(self.__class__.__name__, self._id.value) return "{}(id={})".format(self.__class__.__name__, self._id.value)

View File

@ -4,7 +4,6 @@ import fcntl
import ctypes import ctypes
import warnings import warnings
from os import readv
from ctypes import c_uint16 as _u16 from ctypes import c_uint16 as _u16
from ctypes import c_int16 as _s16 from ctypes import c_int16 as _s16
from ctypes import c_uint32 as _u32 from ctypes import c_uint32 as _u32
@ -22,6 +21,8 @@ from pyglet.input.base import Device, RelativeAxis, AbsoluteAxis, Button, Joysti
from pyglet.input.base import DeviceOpenException, ControllerManager from pyglet.input.base import DeviceOpenException, ControllerManager
from pyglet.input.controller import get_mapping, Relation, create_guid from pyglet.input.controller import get_mapping, Relation, create_guid
c = pyglet.lib.load_library('c')
_IOC_NRBITS = 8 _IOC_NRBITS = 8
_IOC_TYPEBITS = 8 _IOC_TYPEBITS = 8
_IOC_SIZEBITS = 14 _IOC_SIZEBITS = 14
@ -408,7 +409,7 @@ class EvdevDevice(XlibSelectDevice, Device):
return return
try: try:
bytes_read = readv(self._fileno, self._event_buffer) bytes_read = c.read(self._fileno, self._event_buffer, self._event_size)
except OSError: except OSError:
self.close() self.close()
return return

View File

@ -273,9 +273,10 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass):
conventions. This will ensure it is not obscured by other windows, conventions. This will ensure it is not obscured by other windows,
and appears on an appropriate screen for the user. and appears on an appropriate screen for the user.
To render into a window, you must first call `switch_to`, to make To render into a window, you must first call its :py:meth:`.switch_to`
it the current OpenGL context. If you use only one window in the method to make it the active OpenGL context. If you use only one
application, there is no need to do this. window in your application, you can skip this step as it will always
be the active context.
""" """
# Filled in by metaclass with the names of all methods on this (sub)class # Filled in by metaclass with the names of all methods on this (sub)class
@ -638,7 +639,8 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass):
"""Clear the window. """Clear the window.
This is a convenience method for clearing the color and depth This is a convenience method for clearing the color and depth
buffer. The window must be the active context (see `switch_to`). buffer. The window must be the active context (see
:py:meth:`.switch_to`).
""" """
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
@ -646,10 +648,12 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass):
"""Close the window. """Close the window.
After closing the window, the GL context will be invalid. The After closing the window, the GL context will be invalid. The
window instance cannot be reused once closed (see also `set_visible`). window instance cannot be reused once closed. To re-use windows,
see :py:meth:`.set_visible` instead.
The `pyglet.app.EventLoop.on_window_close` event is dispatched on The :py:meth:`pyglet.app.EventLoop.on_window_close` event is
`pyglet.app.event_loop` when this method is called. dispatched by the :py:attr:`pyglet.app.event_loop` when this method
is called.
""" """
from pyglet import app from pyglet import app
if not self._context: if not self._context:
@ -676,7 +680,7 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass):
and advanced applications that must integrate their event loop and advanced applications that must integrate their event loop
into another framework. into another framework.
Typical applications should use `pyglet.app.run`. Typical applications should use :py:func:`pyglet.app.run`.
""" """
raise NotImplementedError('abstract') raise NotImplementedError('abstract')
@ -715,11 +719,14 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass):
"""Swap the OpenGL front and back buffers. """Swap the OpenGL front and back buffers.
Call this method on a double-buffered window to update the Call this method on a double-buffered window to update the
visible display with the back buffer. The contents of the back buffer visible display with the back buffer. Windows are
is undefined after this operation. double-buffered by default unless you turn this feature off.
Windows are double-buffered by default. This method is called The contents of the back buffer are undefined after this operation.
automatically by `EventLoop` after the :py:meth:`~pyglet.window.Window.on_draw` event.
The default :py:attr:`~pyglet.app.event_loop` automatically
calls this method after the window's
:py:meth:`~pyglet.window.Window.on_draw` event.
""" """
raise NotImplementedError('abstract') raise NotImplementedError('abstract')
@ -1158,10 +1165,13 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass):
def switch_to(self): def switch_to(self):
"""Make this window the current OpenGL rendering context. """Make this window the current OpenGL rendering context.
Only one OpenGL context can be active at a time. This method sets Only one OpenGL context can be active at a time. This method
the current window's context to be current. You should use this sets the current window context as the active one.
method in preference to `pyglet.gl.Context.set_current`, as it may
perform additional initialisation functions. In most cases, you should use this method instead of directly
calling :py:meth:`pyglet.gl.Context.set_current`. The latter
will not perform platform-specific state management tasks for
you.
""" """
raise NotImplementedError('abstract') raise NotImplementedError('abstract')