297 lines
9.6 KiB
Python
297 lines
9.6 KiB
Python
# ----------------------------------------------------------------------------
|
|
# pyglet
|
|
# Copyright (c) 2006-2008 Alex Holkner
|
|
# 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.
|
|
# ----------------------------------------------------------------------------
|
|
|
|
'''Application-wide functionality.
|
|
|
|
Most applications need only call `run` after creating one or more windows
|
|
to begin processing events. For example, a simple application consisting of
|
|
one window is::
|
|
|
|
from pyglet import app
|
|
from pyglet import window
|
|
|
|
win = window.Window()
|
|
app.run()
|
|
|
|
To handle events on the main event loop, instantiate it manually. The
|
|
following example exits the application as soon as any window is closed (the
|
|
default policy is to wait until all windows are closed)::
|
|
|
|
event_loop = app.EventLoop()
|
|
|
|
@event_loop.event
|
|
def on_window_close(window):
|
|
event_loop.exit()
|
|
|
|
:since: pyglet 1.1
|
|
'''
|
|
|
|
__docformat__ = 'restructuredtext'
|
|
__version__ = '$Id: __init__.py 2140 2008-07-27 04:15:52Z Alex.Holkner $'
|
|
|
|
import sys
|
|
import weakref
|
|
|
|
from pyglet import clock
|
|
from pyglet import event
|
|
|
|
_is_epydoc = hasattr(sys, 'is_epydoc') and sys.is_epydoc
|
|
|
|
class WeakSet(object):
|
|
'''Set of objects, referenced weakly.
|
|
|
|
Adding an object to this set does not prevent it from being garbage
|
|
collected. Upon being garbage collected, the object is automatically
|
|
removed from the set.
|
|
'''
|
|
def __init__(self):
|
|
self._dict = weakref.WeakKeyDictionary()
|
|
|
|
def add(self, value):
|
|
self._dict[value] = True
|
|
|
|
def remove(self, value):
|
|
del self._dict[value]
|
|
|
|
def __iter__(self):
|
|
for key in self._dict.keys():
|
|
yield key
|
|
|
|
def __contains__(self, other):
|
|
return other in self._dict
|
|
|
|
def __len__(self):
|
|
return len(self._dict)
|
|
|
|
#: Set of all open displays. Instances of `Display` are automatically added
|
|
#: to this set upon construction. The set uses weak references, so displays
|
|
#: are removed from the set when they are no longer referenced.
|
|
#:
|
|
#: :type: `WeakSet`
|
|
displays = WeakSet()
|
|
|
|
#: Set of all open windows (including invisible windows). Instances of
|
|
#: `Window` are automatically added to this set upon construction. The set
|
|
#: uses weak references, so windows are removed from the set when they are no
|
|
#: longer referenced or are closed explicitly.
|
|
#:
|
|
#: :type: `WeakSet`
|
|
windows = WeakSet()
|
|
|
|
|
|
class BaseEventLoop(event.EventDispatcher):
|
|
'''The main run loop of the application.
|
|
|
|
Calling `run` begins the application event loop, which processes
|
|
operating system events, calls `pyglet.clock.tick` to call scheduled
|
|
functions and calls `pyglet.window.Window.on_draw` and
|
|
`pyglet.window.Window.flip` to update window contents.
|
|
|
|
Applications can subclass `EventLoop` and override certain methods
|
|
to integrate another framework's run loop, or to customise processing
|
|
in some other way. You should not in general override `run`, as
|
|
this method contains platform-specific code that ensures the application
|
|
remains responsive to the user while keeping CPU usage to a minimum.
|
|
'''
|
|
|
|
#: Flag indicating if the event loop will exit in the next iteration.
|
|
#: This is
|
|
has_exit = False
|
|
|
|
def run(self):
|
|
'''Begin processing events, scheduled functions and window updates.
|
|
|
|
This method returns when `has_exit` is set to True.
|
|
|
|
Developers are discouraged from overriding this method, as the
|
|
implementation is platform-specific.
|
|
'''
|
|
raise NotImplementedError('abstract')
|
|
|
|
def _setup(self):
|
|
global event_loop
|
|
event_loop = self
|
|
|
|
# Disable event queuing for dispatch_events
|
|
from pyglet.window import Window
|
|
Window._enable_event_queue = False
|
|
|
|
# Dispatch pending events
|
|
for window in windows:
|
|
window.switch_to()
|
|
window.dispatch_pending_events()
|
|
|
|
def _idle_chance(self):
|
|
'''If timeout has expired, manually force an idle loop.
|
|
|
|
Called by window that have blocked the event loop (e.g. during
|
|
resizing).
|
|
'''
|
|
|
|
def idle(self):
|
|
'''Called during each iteration of the event loop.
|
|
|
|
The method is called immediately after any window events (i.e., after
|
|
any user input). The method can return a duration after which
|
|
the idle method will be called again. The method may be called
|
|
earlier if the user creates more input events. The method
|
|
can return `None` to only wait for user events.
|
|
|
|
For example, return ``1.0`` to have the idle method called every
|
|
second, or immediately after any user events.
|
|
|
|
The default implementation dispatches the
|
|
`pyglet.window.Window.on_draw` event for all windows and uses
|
|
`pyglet.clock.tick` and `pyglet.clock.get_sleep_time` on the default
|
|
clock to determine the return value.
|
|
|
|
This method should be overridden by advanced users only. To have
|
|
code execute at regular intervals, use the
|
|
`pyglet.clock.schedule` methods.
|
|
|
|
:rtype: float
|
|
:return: The number of seconds before the idle method should
|
|
be called again, or `None` to block for user input.
|
|
'''
|
|
dt = clock.tick(True)
|
|
|
|
# Redraw all windows
|
|
for window in windows:
|
|
if window.invalid:
|
|
window.switch_to()
|
|
window.dispatch_event('on_draw')
|
|
window.flip()
|
|
|
|
# Update timout
|
|
return clock.get_sleep_time(True)
|
|
|
|
def exit(self):
|
|
'''Safely exit the event loop at the end of the current iteration.
|
|
|
|
This method is convenience for setting `has_exit` to ``True``.
|
|
'''
|
|
self.has_exit = True
|
|
|
|
def on_window_close(self, window):
|
|
'''Default window close handler.'''
|
|
if not windows:
|
|
self.exit()
|
|
|
|
if _is_epydoc:
|
|
def on_window_close(window):
|
|
'''A window was closed.
|
|
|
|
This event is dispatched when a window is closed. It is not
|
|
dispatched if the window's close button was pressed but the
|
|
window did not close.
|
|
|
|
The default handler calls `exit` if no more windows are open. You
|
|
can override this handler to base your application exit on some
|
|
other policy.
|
|
|
|
:event:
|
|
'''
|
|
|
|
def on_enter():
|
|
'''The event loop is about to begin.
|
|
|
|
This is dispatched when the event loop is prepared to enter
|
|
the main run loop, and represents the last chance for an
|
|
application to initialise itself.
|
|
|
|
:event:
|
|
'''
|
|
|
|
def on_exit():
|
|
'''The event loop is about to exit.
|
|
|
|
After dispatching this event, the `run` method returns (the
|
|
application may not actually exit if you have more code
|
|
following the `run` invocation).
|
|
|
|
:event:
|
|
'''
|
|
|
|
BaseEventLoop.register_event_type('on_window_close')
|
|
BaseEventLoop.register_event_type('on_enter')
|
|
BaseEventLoop.register_event_type('on_exit')
|
|
|
|
#: The global event loop. Set to the correct instance when an `EventLoop` is
|
|
#: started.
|
|
#:
|
|
#: :type: `EventLoop`
|
|
event_loop = None
|
|
|
|
def run():
|
|
'''Begin processing events, scheduled functions and window updates.
|
|
|
|
This is a convenience function, equivalent to::
|
|
|
|
EventLoop().run()
|
|
|
|
'''
|
|
EventLoop().run()
|
|
|
|
def exit():
|
|
'''Exit the application event loop.
|
|
|
|
Causes the application event loop to finish, if an event loop is currently
|
|
running. The application may not necessarily exit (for example, there may
|
|
be additional code following the `run` invocation).
|
|
|
|
This is a convenience function, equivalent to::
|
|
|
|
event_loop.exit()
|
|
|
|
'''
|
|
if event_loop:
|
|
event_loop.exit()
|
|
|
|
if _is_epydoc:
|
|
EventLoop = BaseEventLoop
|
|
EventLoop.__name__ = 'EventLoop'
|
|
del BaseEventLoop
|
|
else:
|
|
# Permit cyclic import.
|
|
import pyglet
|
|
pyglet.app = sys.modules[__name__]
|
|
|
|
if sys.platform == 'darwin':
|
|
from pyglet.app.carbon import CarbonEventLoop as EventLoop
|
|
elif sys.platform in ('win32', 'cygwin'):
|
|
from pyglet.app.win32 import Win32EventLoop as EventLoop
|
|
else:
|
|
from pyglet.app.xlib import XlibEventLoop as EventLoop
|
|
|
|
|