From b28ef5c580afebe20311f67ac04c36ed1343d261 Mon Sep 17 00:00:00 2001 From: shenjack <3695888@qq.com> Date: Fri, 3 Nov 2023 16:24:59 +0800 Subject: [PATCH] sync pyglet and lib-not-dr --- libs/lib_not_dr/logger/logger.py | 19 +-- libs/lib_not_dr/logger/outstream.py | 62 +++++++++ libs/pyglet/gl/base.py | 198 ++++++++++++++++++++------- libs/pyglet/gl/lib.py | 3 +- libs/pyglet/graphics/shader.py | 61 +++++---- libs/pyglet/graphics/vertexarray.py | 17 ++- libs/pyglet/graphics/vertexbuffer.py | 21 ++- libs/pyglet/gui/widgets.py | 2 +- libs/pyglet/image/__init__.py | 24 +++- libs/pyglet/image/buffer.py | 35 ++--- libs/pyglet/input/linux/evdev.py | 5 +- libs/pyglet/window/__init__.py | 42 +++--- 12 files changed, 338 insertions(+), 151 deletions(-) create mode 100644 libs/lib_not_dr/logger/outstream.py diff --git a/libs/lib_not_dr/logger/logger.py b/libs/lib_not_dr/logger/logger.py index 762d5d3..255a731 100644 --- a/libs/lib_not_dr/logger/logger.py +++ b/libs/lib_not_dr/logger/logger.py @@ -9,26 +9,11 @@ import inspect from types import FrameType 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 -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): name = 'Logger-v2' diff --git a/libs/lib_not_dr/logger/outstream.py b/libs/lib_not_dr/logger/outstream.py new file mode 100644 index 0000000..d1921ef --- /dev/null +++ b/libs/lib_not_dr/logger/outstream.py @@ -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 diff --git a/libs/pyglet/gl/base.py b/libs/pyglet/gl/base.py index ecec0d6..2f3c075 100644 --- a/libs/pyglet/gl/base.py +++ b/libs/pyglet/gl/base.py @@ -1,6 +1,7 @@ import weakref from enum import Enum +import threading from typing import Tuple import pyglet @@ -209,11 +210,12 @@ class CanvasConfig(Config): class ObjectSpace: def __init__(self): - # Textures and buffers scheduled for deletion - # the next time this object space is active. + # Objects scheduled for deletion the next time this object space is active. self.doomed_textures = [] self.doomed_buffers = [] self.doomed_shader_programs = [] + self.doomed_shaders = [] + self.doomed_renderbuffers = [] class Context: @@ -236,6 +238,7 @@ class Context: self.canvas = None self.doomed_vaos = [] + self.doomed_framebuffers = [] if context_share: self.object_space = context_share.object_space @@ -277,28 +280,44 @@ class Context: self._info = gl_info.GLInfo() 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: - textures = self.object_space.doomed_textures[:] - textures = (gl.GLuint * len(textures))(*textures) - gl.glDeleteTextures(len(textures), textures) - self.object_space.doomed_textures.clear() + self._delete_objects(self.object_space.doomed_textures, gl.glDeleteTextures) if self.object_space.doomed_buffers: - buffers = self.object_space.doomed_buffers[:] - buffers = (gl.GLuint * len(buffers))(*buffers) - gl.glDeleteBuffers(len(buffers), buffers) - self.object_space.doomed_buffers.clear() + self._delete_objects(self.object_space.doomed_buffers, gl.glDeleteBuffers) if self.object_space.doomed_shader_programs: - for program_id in self.object_space.doomed_shader_programs: - gl.glDeleteProgram(program_id) - self.object_space.doomed_shader_programs.clear() + self._delete_objects_one_by_one(self.object_space.doomed_shader_programs, + gl.glDeleteProgram) + 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: - vaos = self.doomed_vaos[:] - vaos = (gl.GLuint * len(vaos))(*vaos) - gl.glDeleteVertexArrays(len(vaos), vaos) - self.doomed_vaos.clear() + self._delete_objects(self.doomed_vaos, gl.glDeleteVertexArrays) + if self.doomed_framebuffers: + self._delete_objects(self.doomed_framebuffers, gl.glDeleteFramebuffers) + + # 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): """Release the context. @@ -318,6 +337,27 @@ class Context: if gl._shadow_window is not None: 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): """Create a ShaderProgram from OpenGL GLSL source. @@ -347,25 +387,30 @@ class Context: return program 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 - ``glDeleteTextures``, however if another context that does not share - this context's object space is currently active, the deletion will - be deferred until an appropriate context is activated. + This method will delete the texture immediately via + ``glDeleteTextures`` if the current context's object space is the same + as this context's object space and it is called from the main thread. + + 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: `texture_id` : int 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)) else: self.object_space.doomed_textures.append(texture_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 ``glDeleteBuffers`` instead of ``glDeleteTextures``. @@ -376,30 +421,14 @@ class Context: .. 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)) else: 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): - """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 ``glDeleteProgram`` instead of ``glDeleteTextures``. @@ -410,11 +439,84 @@ class Context: .. versionadded:: 2.0 """ - if gl.current_context is self: - gl.glDeleteProgram(program_id) + if self._safe_to_operate_on_object_space(): + gl.glDeleteProgram(gl.GLuint(program_id)) else: 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): """Get the OpenGL information for this context. diff --git a/libs/pyglet/gl/lib.py b/libs/pyglet/gl/lib.py index d868430..9704e25 100644 --- a/libs/pyglet/gl/lib.py +++ b/libs/pyglet/gl/lib.py @@ -62,8 +62,7 @@ def errcheck(result, func, arguments): print(name) from pyglet import gl - context = gl.current_context - if not context: + if not gl.current_context: raise GLException('No GL context; create a Window first') error = gl.glGetError() if error: diff --git a/libs/pyglet/graphics/shader.py b/libs/pyglet/graphics/shader.py index a41dddf..cb69d04 100644 --- a/libs/pyglet/graphics/shader.py +++ b/libs/pyglet/graphics/shader.py @@ -674,6 +674,7 @@ class Shader: """ def __init__(self, source_string: str, shader_type: str): + self._context = pyglet.gl.current_context self._id = None self.type = shader_type @@ -689,6 +690,7 @@ class Shader: source_length = c_int(len(shader_source_utf8)) shader_id = glCreateShader(shader_type) + self._id = shader_id glShaderSource(shader_id, 1, byref(source_buffer_pointer), source_length) glCompileShader(shader_id) @@ -709,8 +711,6 @@ class Shader: elif _debug_gl_shaders: print(self._get_shader_log(shader_id)) - self._id = shader_id - @property def id(self): return self._id @@ -735,16 +735,19 @@ class Shader: glGetShaderSource(shader_id, source_length, None, source_str) return source_str.value.decode('utf8') - def __del__(self): - try: - glDeleteShader(self._id) - if _debug_gl_shaders: - print(f"Destroyed {self.type} Shader '{self._id}'") + def delete(self): + glDeleteShader(self._id) + self._id = None - except Exception: - # Interpreter is shutting down, - # or Shader failed to compile. - pass + def __del__(self): + if self._id is not None: + try: + 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): 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__' def __init__(self, *shaders: Shader): + self._id = None + assert shaders, "At least one Shader object is required." self._id = _link_program(*shaders) self._context = pyglet.gl.current_context @@ -799,13 +804,17 @@ class ShaderProgram: def __exit__(self, *_): glUseProgram(0) + def delete(self): + glDeleteProgram(self._id) + self._id = None + def __del__(self): - try: - self._context.delete_shader_program(self.id) - except Exception: - # Interpreter is shutting down, - # or ShaderProgram failed to link. - pass + if self._id is not None: + try: + self._context.delete_shader_program(self._id) + self._id = None + except (AttributeError, ImportError): + pass # Interpreter is shutting down def __setitem__(self, key, value): try: @@ -930,6 +939,8 @@ class ComputeShaderProgram: def __init__(self, source: str): """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")): 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.") @@ -1002,13 +1013,17 @@ class ComputeShaderProgram: def __exit__(self, *_): glUseProgram(0) + def delete(self): + glDeleteProgram(self._id) + self._id = None + def __del__(self): - try: - self._context.delete_shader_program(self.id) - except Exception: - # Interpreter is shutting down, - # or ShaderProgram failed to link. - pass + if self._id is not None: + try: + self._context.delete_shader_program(self._id) + self._id = None + except (AttributeError, ImportError): + pass # Interpreter is shutting down def __setitem__(self, key, value): try: diff --git a/libs/pyglet/graphics/vertexarray.py b/libs/pyglet/graphics/vertexarray.py index e4c2580..4071a50 100644 --- a/libs/pyglet/graphics/vertexarray.py +++ b/libs/pyglet/graphics/vertexarray.py @@ -27,10 +27,8 @@ class VertexArray: glBindVertexArray(0) def delete(self): - try: - glDeleteVertexArrays(1, self._id) - except Exception: - pass + glDeleteVertexArrays(1, self._id) + self._id = None __enter__ = bind @@ -38,11 +36,12 @@ class VertexArray: glBindVertexArray(0) def __del__(self): - try: - self._context.delete_vao(self.id) - # Python interpreter is shutting down: - except ImportError: - pass + if self._id is not None: + try: + self._context.delete_vao(self.id) + self._id = None + except (ImportError, AttributeError): + pass # Interpreter is shutting down def __repr__(self): return "{}(id={})".format(self.__class__.__name__, self._id.value) diff --git a/libs/pyglet/graphics/vertexbuffer.py b/libs/pyglet/graphics/vertexbuffer.py index fc92380..cd3f864 100644 --- a/libs/pyglet/graphics/vertexbuffer.py +++ b/libs/pyglet/graphics/vertexbuffer.py @@ -164,21 +164,18 @@ class BufferObject(AbstractBuffer): def unmap(self): 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): - buffer_id = GLuint(self.id) - try: - glDeleteBuffers(1, buffer_id) - except Exception: - pass + glDeleteBuffers(1, self.id) 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): # Map, create a copy, then reinitialize. temp = (ctypes.c_byte * size)() diff --git a/libs/pyglet/gui/widgets.py b/libs/pyglet/gui/widgets.py index 010953c..f0b5807 100644 --- a/libs/pyglet/gui/widgets.py +++ b/libs/pyglet/gui/widgets.py @@ -452,7 +452,7 @@ class TextEntry(WidgetBase): self._height = value self._layout.height = value self._outline.height = value - + @property def focus(self) -> bool: return self._focus diff --git a/libs/pyglet/image/__init__.py b/libs/pyglet/image/__init__.py index e9a6431..0ef0592 100644 --- a/libs/pyglet/image/__init__.py +++ b/libs/pyglet/image/__init__.py @@ -1220,11 +1220,20 @@ class Texture(AbstractImage): self.id = tex_id 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): - try: - self._context.delete_texture(self.id) - except Exception: - pass + if self.id is not None: + try: + self._context.delete_texture(self.id) + self.id = None + except (AttributeError, ImportError): + pass # Interpreter is shutting down def bind(self, texture_unit: int = 0): """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, 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): - # only the owner Texture should handle deletion pass diff --git a/libs/pyglet/image/buffer.py b/libs/pyglet/image/buffer.py index c2ffdbd..7d02f8a 100644 --- a/libs/pyglet/image/buffer.py +++ b/libs/pyglet/image/buffer.py @@ -13,6 +13,7 @@ class Renderbuffer: def __init__(self, width, height, internal_format, samples=1): """Create an instance of a Renderbuffer object.""" + self._context = pyglet.gl.current_context self._id = GLuint() self._width = width self._height = height @@ -49,13 +50,15 @@ class Renderbuffer: def delete(self): glDeleteRenderbuffers(1, self._id) + self._id = None def __del__(self): - try: - glDeleteRenderbuffers(1, self._id) - # Python interpreter is shutting down: - except Exception: - pass + if self._id is not None: + try: + self._context.delete_renderbuffer(self._id.value) + self._id = None + except (AttributeError, ImportError): + pass # Interpreter is shutting down def __repr__(self): return "{}(id={})".format(self.__class__.__name__, self._id.value) @@ -71,6 +74,7 @@ class Framebuffer: .. versionadded:: 2.0 """ + self._context = pyglet.gl.current_context self._id = GLuint() glGenFramebuffers(1, self._id) self._attachment_types = 0 @@ -105,10 +109,16 @@ class Framebuffer: self.unbind() def delete(self): - try: - glDeleteFramebuffers(1, self._id) - except Exception: - pass + glDeleteFramebuffers(1, self._id) + self._id = None + + 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 def is_complete(self): @@ -203,12 +213,5 @@ class Framebuffer: self._height = max(renderbuffer.height, self._height) self.unbind() - def __del__(self): - try: - glDeleteFramebuffers(1, self._id) - # Python interpreter is shutting down: - except Exception: - pass - def __repr__(self): return "{}(id={})".format(self.__class__.__name__, self._id.value) diff --git a/libs/pyglet/input/linux/evdev.py b/libs/pyglet/input/linux/evdev.py index 5c00fee..7bda2f3 100644 --- a/libs/pyglet/input/linux/evdev.py +++ b/libs/pyglet/input/linux/evdev.py @@ -4,7 +4,6 @@ import fcntl import ctypes import warnings -from os import readv from ctypes import c_uint16 as _u16 from ctypes import c_int16 as _s16 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.controller import get_mapping, Relation, create_guid +c = pyglet.lib.load_library('c') + _IOC_NRBITS = 8 _IOC_TYPEBITS = 8 _IOC_SIZEBITS = 14 @@ -408,7 +409,7 @@ class EvdevDevice(XlibSelectDevice, Device): return try: - bytes_read = readv(self._fileno, self._event_buffer) + bytes_read = c.read(self._fileno, self._event_buffer, self._event_size) except OSError: self.close() return diff --git a/libs/pyglet/window/__init__.py b/libs/pyglet/window/__init__.py index 2974c6b..aed30e8 100644 --- a/libs/pyglet/window/__init__.py +++ b/libs/pyglet/window/__init__.py @@ -273,9 +273,10 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass): conventions. This will ensure it is not obscured by other windows, and appears on an appropriate screen for the user. - To render into a window, you must first call `switch_to`, to make - it the current OpenGL context. If you use only one window in the - application, there is no need to do this. + To render into a window, you must first call its :py:meth:`.switch_to` + method to make it the active OpenGL context. If you use only one + 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 @@ -638,7 +639,8 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass): """Clear the window. 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) @@ -646,10 +648,12 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass): """Close the window. 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 - `pyglet.app.event_loop` when this method is called. + The :py:meth:`pyglet.app.EventLoop.on_window_close` event is + dispatched by the :py:attr:`pyglet.app.event_loop` when this method + is called. """ from pyglet import app if not self._context: @@ -676,7 +680,7 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass): and advanced applications that must integrate their event loop into another framework. - Typical applications should use `pyglet.app.run`. + Typical applications should use :py:func:`pyglet.app.run`. """ raise NotImplementedError('abstract') @@ -715,11 +719,14 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass): """Swap the OpenGL front and back buffers. Call this method on a double-buffered window to update the - visible display with the back buffer. The contents of the back buffer - is undefined after this operation. + visible display with the back buffer. Windows are + double-buffered by default unless you turn this feature off. - Windows are double-buffered by default. This method is called - automatically by `EventLoop` after the :py:meth:`~pyglet.window.Window.on_draw` event. + The contents of the back buffer are undefined after this operation. + + 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') @@ -1158,10 +1165,13 @@ class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass): def switch_to(self): """Make this window the current OpenGL rendering context. - Only one OpenGL context can be active at a time. This method sets - the current window's context to be current. You should use this - method in preference to `pyglet.gl.Context.set_current`, as it may - perform additional initialisation functions. + Only one OpenGL context can be active at a time. This method + sets the current window context as the active one. + + 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')