365 lines
12 KiB
Python
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."""
|