# ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner # Copyright (c) 2008-2021 pyglet contributors # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # * Neither the name of pyglet nor the names of its # contributors may be used to endorse or promote products # derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- """pyglet is a cross-platform games and multimedia package. More information is available at http://www.pyglet.org """ import os import sys from typing import TYPE_CHECKING #: The release version version = '2.0.a3' __version__ = version if sys.version_info < (3, 6): raise Exception('pyglet %s requires Python 3.6 or newer.' % version) if 'sphinx' in sys.modules: setattr(sys, 'is_pyglet_doc_run', True) _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run # pyglet platform treats *BSD systems as Linux compat_platform = sys.platform if "bsd" in compat_platform: compat_platform = "linux-compat" _enable_optimisations = not __debug__ if getattr(sys, 'frozen', None): _enable_optimisations = True #: Global dict of pyglet options. To change an option from its default, you #: must import ``pyglet`` before any sub-packages. For example:: #: #: import pyglet #: pyglet.options['debug_gl'] = False #: #: The default options can be overridden from the OS environment. The #: corresponding environment variable for each option key is prefaced by #: ``PYGLET_``. For example, in Bash you can set the ``debug_gl`` option with:: #: #: PYGLET_DEBUG_GL=True; export PYGLET_DEBUG_GL #: #: For options requiring a tuple of values, separate each value with a comma. #: #: The non-development options are: #: #: audio #: A sequence of the names of audio modules to attempt to load, in #: order of preference. Valid driver names are: #: #: * xaudio2, the Windows Xaudio2 audio module (Windows only) #: * directsound, the Windows DirectSound audio module (Windows only) #: * pulse, the PulseAudio module (Linux only) #: * openal, the OpenAL audio module #: * silent, no audio #: debug_lib #: If True, prints the path of each dynamic library loaded. #: debug_gl #: If True, all calls to OpenGL functions are checked afterwards for #: errors using ``glGetError``. This will severely impact performance, #: but provides useful exceptions at the point of failure. By default, #: this option is enabled if ``__debug__`` is (i.e., if Python was not run #: with the -O option). It is disabled by default when pyglet is "frozen" #: within a py2exe or py2app library archive. #: shadow_window #: By default, pyglet creates a hidden window with a GL context when #: pyglet.gl is imported. This allows resources to be loaded before #: the application window is created, and permits GL objects to be #: shared between windows even after they've been closed. You can #: disable the creation of the shadow window by setting this option to #: False. #: #: Some OpenGL driver implementations may not support shared OpenGL #: contexts and may require disabling the shadow window (and all resources #: must be loaded after the window using them was created). Recommended #: for advanced developers only. #: #: .. versionadded:: 1.1 #: vsync #: If set, the `pyglet.window.Window.vsync` property is ignored, and #: this option overrides it (to either force vsync on or off). If unset, #: or set to None, the `pyglet.window.Window.vsync` property behaves #: as documented. #: xsync #: If set (the default), pyglet will attempt to synchronise the drawing of #: double-buffered windows to the border updates of the X11 window #: manager. This improves the appearance of the window during resize #: operations. This option only affects double-buffered windows on #: X11 servers supporting the Xsync extension with a window manager #: that implements the _NET_WM_SYNC_REQUEST protocol. #: #: .. versionadded:: 1.1 #: search_local_libs #: If False, pyglet won't try to search for libraries in the script #: directory and its `lib` subdirectory. This is useful to load a local #: library instead of the system installed version. This option is set #: to True by default. #: #: .. versionadded:: 1.2pyglet #: options = { 'audio': ('xaudio2', 'directsound', 'openal', 'pulse', 'silent'), 'debug_font': False, 'debug_gl': not _enable_optimisations, 'debug_gl_trace': False, 'debug_gl_trace_args': False, 'debug_gl_shaders': False, 'debug_graphics_batch': False, 'debug_lib': False, 'debug_media': False, 'debug_texture': False, 'debug_trace': False, 'debug_trace_args': False, 'debug_trace_depth': 1, 'debug_trace_flush': True, 'debug_win32': False, 'debug_x11': False, 'shadow_window': True, 'vsync': None, 'xsync': True, 'xlib_fullscreen_override_redirect': False, 'search_local_libs': True, 'win32_gdi_font': False, 'headless': False, 'headless_device': 0, 'win32_disable_shaping': False, } _option_types = { 'audio': tuple, 'debug_font': bool, 'debug_gl': bool, 'debug_gl_trace': bool, 'debug_gl_trace_args': bool, 'debug_gl_shaders': bool, 'debug_graphics_batch': bool, 'debug_lib': bool, 'debug_media': bool, 'debug_texture': bool, 'debug_trace': bool, 'debug_trace_args': bool, 'debug_trace_depth': int, 'debug_trace_flush': bool, 'debug_win32': bool, 'debug_x11': bool, 'shadow_window': bool, 'vsync': bool, 'xsync': bool, 'xlib_fullscreen_override_redirect': bool, 'search_local_libs': bool, 'win32_gdi_font': bool, 'headless': bool, 'headless_device': int, 'win32_disable_shaping': bool } def _read_environment(): """Read defaults for options from environment""" for key in options: assert key in _option_types, f"Option '{key}' must have a type set in _option_types." env = 'PYGLET_%s' % key.upper() try: value = os.environ[env] if _option_types[key] is tuple: options[key] = value.split(',') elif _option_types[key] is bool: options[key] = value in ('true', 'TRUE', 'True', '1') elif _option_types[key] is int: options[key] = int(value) except KeyError: pass _read_environment() if compat_platform == 'cygwin': # This hack pretends that the posix-like ctypes provides windows # functionality. COM does not work with this hack, so there is no # DirectSound support. import ctypes ctypes.windll = ctypes.cdll ctypes.oledll = ctypes.cdll ctypes.WINFUNCTYPE = ctypes.CFUNCTYPE ctypes.HRESULT = ctypes.c_long # Call tracing # ------------ _trace_filename_abbreviations = {} def _trace_repr(value, size=40): value = repr(value) if len(value) > size: value = value[:size // 2 - 2] + '...' + value[-size // 2 - 1:] return value def _trace_frame(thread, frame, indent): from pyglet import lib if frame.f_code is lib._TraceFunction.__call__.__code__: is_ctypes = True func = frame.f_locals['self']._func name = func.__name__ location = '[ctypes]' else: is_ctypes = False code = frame.f_code name = code.co_name path = code.co_filename line = code.co_firstlineno try: filename = _trace_filename_abbreviations[path] except KeyError: # Trim path down dir = '' path, filename = os.path.split(path) while len(dir + filename) < 30: filename = os.path.join(dir, filename) path, dir = os.path.split(path) if not dir: filename = os.path.join('', filename) break else: filename = os.path.join('...', filename) _trace_filename_abbreviations[path] = filename location = '(%s:%d)' % (filename, line) if indent: name = 'Called from %s' % name print('[%d] %s%s %s' % (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))) 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)) except: pass if _trace_flush: sys.stdout.flush() def _thread_trace_func(thread): def _trace_func(frame, event, arg): if event == 'call': indent = '' for i in range(_trace_depth): _trace_frame(thread, frame, indent) indent += ' ' frame = frame.f_back if not frame: break elif event == 'exception': (exception, value, traceback) = arg print('First chance exception raised:', repr(exception)) return _trace_func def _install_trace(): global _trace_thread_count sys.setprofile(_thread_trace_func(_trace_thread_count)) _trace_thread_count += 1 _trace_thread_count = 0 _trace_args = options['debug_trace_args'] _trace_depth = options['debug_trace_depth'] _trace_flush = options['debug_trace_flush'] if options['debug_trace']: _install_trace() # Lazy loading # ------------ class _ModuleProxy: _module = None def __init__(self, name): self.__dict__['_module_name'] = name def __getattr__(self, name): try: return getattr(self._module, name) except AttributeError: if self._module is not None: raise import_name = 'pyglet.%s' % self._module_name __import__(import_name) module = sys.modules[import_name] object.__setattr__(self, '_module', module) globals()[self._module_name] = module return getattr(module, name) def __setattr__(self, name, value): try: setattr(self._module, name, value) except AttributeError: if self._module is not None: raise import_name = 'pyglet.%s' % self._module_name __import__(import_name) module = sys.modules[import_name] object.__setattr__(self, '_module', module) globals()[self._module_name] = module setattr(module, name, value) # Lazily load all modules, except if performing # type checking or code inspection. if TYPE_CHECKING: from . import app from . import canvas from . import clock from . import com from . import event from . import font from . import gl from . import graphics from . import gui from . import input from . import image from . import lib from . import math from . import media from . import model from . import resource from . import sprite from . import shapes from . import text from . import window else: app = _ModuleProxy('app') canvas = _ModuleProxy('canvas') clock = _ModuleProxy('clock') com = _ModuleProxy('com') event = _ModuleProxy('event') font = _ModuleProxy('font') gl = _ModuleProxy('gl') graphics = _ModuleProxy('graphics') gui = _ModuleProxy('gui') image = _ModuleProxy('image') input = _ModuleProxy('input') lib = _ModuleProxy('lib') math = _ModuleProxy('math') media = _ModuleProxy('media') model = _ModuleProxy('model') resource = _ModuleProxy('resource') sprite = _ModuleProxy('sprite') shapes = _ModuleProxy('shapes') text = _ModuleProxy('text') window = _ModuleProxy('window')