Difficult-Rocket/libs/pyglet/canvas/base.py
2022-10-23 19:56:14 +08:00

365 lines
12 KiB
Python

# ----------------------------------------------------------------------------
# pyglet
# Copyright (c) 2006-2008 Alex Holkner
# Copyright (c) 2008-2022 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.
# ----------------------------------------------------------------------------
from pyglet import gl
from pyglet import app
from pyglet import window
from pyglet import canvas
class Display:
"""A display device supporting one or more screens.
.. versionadded:: 1.2
"""
name = None
"""Name of this display, if applicable.
:type: str
"""
x_screen = None
"""The X11 screen number of this display, if applicable.
:type: int
"""
def __init__(self, name=None, x_screen=None):
"""Create a display connection for the given name and screen.
On X11, :attr:`name` is of the form ``"hostname:display"``, where the
default is usually ``":1"``. On X11, :attr:`x_screen` gives the X
screen number to use with this display. A pyglet display can only be
used with one X screen; open multiple display connections to access
multiple X screens.
Note that TwinView, Xinerama, xrandr and other extensions present
multiple monitors on a single X screen; this is usually the preferred
mechanism for working with multiple monitors under X11 and allows each
screen to be accessed through a single pyglet`~pyglet.canvas.Display`
On platforms other than X11, :attr:`name` and :attr:`x_screen` are
ignored; there is only a single display device on these systems.
:Parameters:
name : str
The name of the display to connect to.
x_screen : int
The X11 screen number to use.
"""
canvas._displays.add(self)
def get_screens(self):
"""Get the available screens.
A typical multi-monitor workstation comprises one :class:`Display`
with multiple :class:`Screen` s. This method returns a list of
screens which can be enumerated to select one for full-screen display.
For the purposes of creating an OpenGL config, the default screen
will suffice.
:rtype: list of :class:`Screen`
"""
raise NotImplementedError('abstract')
def get_default_screen(self):
"""Get the default screen as specified by the user's operating system
preferences.
:rtype: :class:`Screen`
"""
return self.get_screens()[0]
def get_windows(self):
"""Get the windows currently attached to this display.
:rtype: sequence of :class:`~pyglet.window.Window`
"""
return [window for window in app.windows if window.display is self]
class Screen:
"""A virtual monitor that supports fullscreen windows.
Screens typically map onto a physical display such as a
monitor, television or projector. Selecting a screen for a window
has no effect unless the window is made fullscreen, in which case
the window will fill only that particular virtual screen.
The :attr:`width` and :attr:`height` attributes of a screen give the
current resolution of the screen. The :attr:`x` and :attr:`y` attributes
give the global location of the top-left corner of the screen. This is
useful for determining if screens are arranged above or next to one
another.
Use :func:`~Display.get_screens` or :func:`~Display.get_default_screen`
to obtain an instance of this class.
"""
def __init__(self, display, x, y, width, height):
"""
:parameters:
`display` : `~pyglet.canvas.Display`
:attr:`display`
`x` : int
Left edge :attr:`x`
`y` : int
Top edge :attr:`y`
`width` : int
:attr:`width`
`height` : int
:attr:`height`
"""
self.display = display
"""Display this screen belongs to."""
self.x = x
"""Left edge of the screen on the virtual desktop."""
self.y = y
"""Top edge of the screen on the virtual desktop."""
self.width = width
"""Width of the screen, in pixels."""
self.height = height
"""Height of the screen, in pixels."""
def __repr__(self):
return '{}(x={}, y={}, width={}, height={})'.format(self.__class__.__name__, self.x, self.y, self.width, self.height)
def get_best_config(self, template=None):
"""Get the best available GL config.
Any required attributes can be specified in `template`. If
no configuration matches the template,
:class:`~pyglet.window.NoSuchConfigException` will be raised.
:deprecated: Use :meth:`pyglet.gl.Config.match`.
:Parameters:
`template` : `pyglet.gl.Config`
A configuration with desired attributes filled in.
:rtype: :class:`~pyglet.gl.Config`
:return: A configuration supported by the platform that best
fulfils the needs described by the template.
"""
configs = None
if template is None:
for template_config in [gl.Config(double_buffer=True, depth_size=24, major_version=3, minor_version=3),
gl.Config(double_buffer=True, depth_size=16, major_version=3, minor_version=3),
None]:
try:
configs = self.get_matching_configs(template_config)
break
except window.NoSuchConfigException:
pass
else:
configs = self.get_matching_configs(template)
if not configs:
raise window.NoSuchConfigException()
return configs[0]
def get_matching_configs(self, template):
"""Get a list of configs that match a specification.
Any attributes specified in `template` will have values equal
to or greater in each returned config. If no configs satisfy
the template, an empty list is returned.
:deprecated: Use :meth:`pyglet.gl.Config.match`.
:Parameters:
`template` : `pyglet.gl.Config`
A configuration with desired attributes filled in.
:rtype: list of :class:`~pyglet.gl.Config`
:return: A list of matching configs.
"""
raise NotImplementedError('abstract')
def get_modes(self):
"""Get a list of screen modes supported by this screen.
:rtype: list of :class:`ScreenMode`
.. versionadded:: 1.2
"""
raise NotImplementedError('abstract')
def get_mode(self):
"""Get the current display mode for this screen.
:rtype: :class:`ScreenMode`
.. versionadded:: 1.2
"""
raise NotImplementedError('abstract')
def get_closest_mode(self, width, height):
"""Get the screen mode that best matches a given size.
If no supported mode exactly equals the requested size, a larger one
is returned; or ``None`` if no mode is large enough.
:Parameters:
`width` : int
Requested screen width.
`height` : int
Requested screen height.
:rtype: :class:`ScreenMode`
.. versionadded:: 1.2
"""
# Best mode is one with smallest resolution larger than width/height,
# with depth and refresh rate equal to current mode.
current = self.get_mode()
best = None
for mode in self.get_modes():
# Reject resolutions that are too small
if mode.width < width or mode.height < height:
continue
if best is None:
best = mode
# Must strictly dominate dimensions
if (mode.width <= best.width and mode.height <= best.height and
(mode.width < best.width or mode.height < best.height)):
best = mode
# Preferably match rate, then depth.
if mode.width == best.width and mode.height == best.height:
points = 0
if mode.rate == current.rate:
points += 2
if best.rate == current.rate:
points -= 2
if mode.depth == current.depth:
points += 1
if best.depth == current.depth:
points -= 1
if points > 0:
best = mode
return best
def set_mode(self, mode):
"""Set the display mode for this screen.
The mode must be one previously returned by :meth:`get_mode` or
:meth:`get_modes`.
:Parameters:
`mode` : `ScreenMode`
Screen mode to switch this screen to.
"""
raise NotImplementedError('abstract')
def restore_mode(self):
"""Restore the screen mode to the user's default.
"""
raise NotImplementedError('abstract')
class ScreenMode:
"""Screen resolution and display settings.
Applications should not construct `ScreenMode` instances themselves; see
:meth:`Screen.get_modes`.
The :attr:`depth` and :attr:`rate` variables may be ``None`` if the
operating system does not provide relevant data.
.. versionadded:: 1.2
"""
width = None
"""Width of screen, in pixels.
:type: int
"""
height = None
"""Height of screen, in pixels.
:type: int
"""
depth = None
"""Pixel color depth, in bits per pixel.
:type: int
"""
rate = None
"""Screen refresh rate in Hz.
:type: int
"""
def __init__(self, screen):
"""
:parameters:
`screen` : `Screen`
"""
self.screen = screen
def __repr__(self):
return f'{self.__class__.__name__}(width={self.width!r}, height={self.height!r}, depth={self.depth!r}, rate={self.rate})'
class Canvas:
"""Abstract drawing area.
Canvases are used internally by pyglet to represent drawing areas --
either within a window or full-screen.
.. versionadded:: 1.2
"""
def __init__(self, display):
"""
:parameters:
`display` : `Display`
:attr:`display`
"""
self.display = display
"""Display this canvas was created on."""