summaryrefslogtreecommitdiff
path: root/unrpyc/renpy/display/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'unrpyc/renpy/display/core.py')
-rw-r--r--unrpyc/renpy/display/core.py2463
1 files changed, 0 insertions, 2463 deletions
diff --git a/unrpyc/renpy/display/core.py b/unrpyc/renpy/display/core.py
deleted file mode 100644
index 4731df0..0000000
--- a/unrpyc/renpy/display/core.py
+++ /dev/null
@@ -1,2463 +0,0 @@
-# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
-#
-# Permission is hereby granted, free of charge, to any person
-# obtaining a copy of this software and associated documentation files
-# (the "Software"), to deal in the Software without restriction,
-# including without limitation the rights to use, copy, modify, merge,
-# publish, distribute, sublicense, and/or sell copies of the Software,
-# and to permit persons to whom the Software is furnished to do so,
-# subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-# This file contains code for initializing and managing the display
-# window.
-
-import renpy.display
-import renpy.audio
-import renpy.text
-
-import pygame #@UnusedImport
-
-import sys
-import os
-import time
-import io
-import threading
-
-try:
- import android #@UnresolvedImport @UnusedImport
- import android.sound #@UnresolvedImport
-except:
- android = None
-
-# Is the cpu idle enough to do other things?
-cpu_idle = threading.Event()
-cpu_idle.clear()
-
-# Need to be +4, so we don't interfere with FFMPEG's events.
-TIMEEVENT = pygame.USEREVENT + 4
-PERIODIC = pygame.USEREVENT + 5
-JOYEVENT = pygame.USEREVENT + 6
-REDRAW = pygame.USEREVENT + 7
-
-# All events except for TIMEEVENT and REDRAW
-ALL_EVENTS = [ i for i in range(0, REDRAW + 1) if i != TIMEEVENT and i != REDRAW ]
-
-# The number of msec between periodic events.
-PERIODIC_INTERVAL = 50
-
-# Time management.
-time_base = None
-
-def init_time():
- global time_base
- time_base = time.time() - pygame.time.get_ticks() / 1000.0
-
-def get_time():
- return time_base + pygame.time.get_ticks() / 1000.0
-
-
-def displayable_by_tag(layer, tag):
- """
- Get the displayable on the given layer with the given tag.
- """
-
- return renpy.game.context().scene_lists.get_displayable_by_tag(layer, tag)
-
-class IgnoreEvent(Exception):
- """
- Exception that is raised when we want to ignore an event, but
- also don't want to return anything.
- """
-
- pass
-
-class EndInteraction(Exception):
- """
- Exception that can be raised (for example, during the render method of
- a displayable) to end the current interaction immediately.
- """
-
- def __init__(self, value):
- self.value = value
-
-class absolute(float):
- """
- This represents an absolute float coordinate.
- """
- __slots__ = [ ]
-
-
-class Displayable(renpy.object.Object):
- """
- The base class for every object in Ren'Py that can be
- displayed to the screen.
-
- Drawables will be serialized to a savegame file. Therefore, they
- shouldn't store non-serializable things (like pygame surfaces) in
- their fields.
- """
-
- # Some invariants about method call order:
- #
- # per_interact is called before render.
- # render is called before event.
- #
- # get_placement can be called at any time, so can't
- # assume anything.
-
- activated = False
- focusable = False
- full_focus_name = None
- role = ''
-
- # The event we'll pass on to our parent transform.
- transform_event = None
-
- # Can we change our look in response to transform_events?
- transform_event_responder = False
-
- def __init__(self, focus=None, default=False, style='default', **properties): # W0231
- self.style = renpy.style.Style(style, properties, heavy=True)
- self.focus_name = focus
- self.default = default
-
- def find_focusable(self, callback, focus_name):
-
- focus_name = self.focus_name or focus_name
-
- if self.focusable:
- callback(self, focus_name)
-
- for i in self.visit():
- if i is None:
- continue
-
- i.find_focusable(callback, focus_name)
-
-
- def focus(self, default=False):
- """
- Called to indicate that this widget has the focus.
- """
-
- if not self.activated:
- self.set_style_prefix(self.role + "hover_", True)
-
- if not default and not self.activated:
- if self.style.sound:
- renpy.audio.music.play(self.style.sound, channel="sound")
-
- def unfocus(self, default=False):
- """
- Called to indicate that this widget has become unfocused.
- """
-
- if not self.activated:
- self.set_style_prefix(self.role + "idle_", True)
-
- def is_focused(self):
-
- if renpy.display.focus.grab and renpy.display.focus.grab is not self:
- return
-
- return renpy.game.context().scene_lists.focused is self
-
- def set_style_prefix(self, prefix, root):
- """
- Called to set the style prefix of this widget and its child
- widgets, if any.
-
- `root` - True if this is the root of a style tree, False if this
- has been passed on to a child.
- """
-
- if prefix == self.style.prefix:
- return
-
- self.style.set_prefix(prefix)
- renpy.display.render.redraw(self, 0)
-
- def parameterize(self, name, parameters):
- """
- Called to parameterize this. By default, we don't take any
- parameters.
- """
-
- if parameters:
- raise Exception("Image '%s' can't take parameters '%s'. (Perhaps you got the name wrong?)" %
- (' '.join(name), ' '.join(parameters)))
-
- return self
-
- def render(self, width, height, st, at):
- """
- Called to display this displayable. This is called with width
- and height parameters, which give the largest width and height
- that this drawable can be drawn to without overflowing some
- bounding box. It's also given two times. It returns a Surface
- that is the current image of this drawable.
-
- @param st: The time since this widget was first shown, in seconds.
- @param at: The time since a similarly named widget was first shown,
- in seconds.
- """
-
- assert False, "Draw not implemented."
-
- def event(self, ev, x, y, st):
- """
- Called to report than an event has occured. Ev is the raw
- pygame event object representing that event. If the event
- involves the mouse, x and y are the translation of the event
- into the coordinates of this displayable. st is the time this
- widget has been shown for.
-
- @returns A value that should be returned from Interact, or None if
- no value is appropriate.
- """
-
- return None
-
- def get_placement(self):
- """
- Returns a style object containing placement information for
- this Displayable. Children are expected to overload this
- to return something more sensible.
- """
-
- return self.style.get_placement()
-
- def visit_all(self, callback):
- """
- Calls the callback on this displayable and all children of this
- displayable.
- """
-
- for d in self.visit():
- if not d:
- continue
- d.visit_all(callback)
-
- callback(self)
-
- def visit(self):
- """
- Called to ask the displayable to return a list of its children
- (including children taken from styles). For convenience, this
- list may also include None values.
- """
-
- return [ ]
-
- def per_interact(self):
- """
- Called once per widget per interaction.
- """
-
- return None
-
- def predict_one(self):
- """
- Called to ask this displayable to call the callback with all
- the images it may want to load.
- """
-
- return
-
- def predict_one_action(self):
- """
- Called to ask this displayable to cause image prediction
- to occur for images that may be loaded by its actions.
- """
-
- return
-
- def place(self, dest, x, y, width, height, surf, main=True):
- """
- This draws this Displayable onto a destination surface, using
- the placement style information returned by this object's
- get_placement() method.
-
- @param dest: The surface that this displayable will be drawn
- on.
-
- @param x: The minimum x coordinate on this surface that this
- Displayable will be drawn to.
-
- @param y: The minimum y coordinate on this surface that this
- displayable will be drawn to.
-
- @param width: The width of the area allocated to this
- Displayable.
-
- @param height: The height of the area allocated to this
- Displayable.
-
- @param surf: The surface returned by a previous call to
- self.render().
- """
-
- xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = self.get_placement()
-
- if xpos is None:
- xpos = 0
- if ypos is None:
- ypos = 0
- if xanchor is None:
- xanchor = 0
- if yanchor is None:
- yanchor = 0
- if xoffset is None:
- xoffset = 0
- if yoffset is None:
- yoffset = 0
-
- # We need to use type, since isinstance(absolute(0), float).
- if xpos.__class__ is float:
- xpos *= width
-
- if xanchor.__class__ is float:
- xanchor *= surf.width
-
- xpos += x + xoffset - xanchor
-
- # y
-
- if ypos.__class__ is float:
- ypos *= height
-
- if yanchor.__class__ is float:
- yanchor *= surf.height
-
- ypos += y + yoffset - yanchor
-
- if dest is not None:
- if subpixel:
- dest.subpixel_blit(surf, (xpos, ypos), main, main, None)
- else:
- dest.blit(surf, (xpos, ypos), main, main, None)
-
- return xpos, ypos
-
- def set_transform_event(self, event):
- """
- Sets the transform event of this displayable to event.
- """
-
- if event == self.transform_event:
- return
-
- self.transform_event = event
- if self.transform_event_responder:
- renpy.display.render.redraw(self, 0)
-
- def _hide(self, st, at, kind):
- """
- Returns None if this displayable is ready to be hidden, or
- a replacement displayable if it doesn't want to be hidden
- quite yet. Kind is either "hide" or "replaced".
- """
-
- return None
-
- def _show(self):
- """
- Called when the displayable is added to a scene list.
- """
-
- def _get_parameterized(self):
- """
- If this is a ImageReference to a parameterized image, return
- the get_parameterized() of the parameterized image. Otherwise,
- return this displayable.
- """
-
- return self
-
- def _change_transform_child(self, child):
- """
- If this is a transform, makes a copy of the transform and sets
- the child of the innermost transform to this. Otherwise,
- simply returns child.
- """
-
- return child
-
-
-
-class SceneListEntry(renpy.object.Object):
- """
- Represents a scene list entry. Since this was replacing a tuple,
- it should be treated as immutable after its initial creation.
- """
-
- def __init__(self, tag, zorder, show_time, animation_time, displayable, name):
- self.tag = tag
- self.zorder = zorder
- self.show_time = show_time
- self.animation_time = animation_time
- self.displayable = displayable
- self.name = name
-
- def __iter__(self):
- return iter((self.tag, self.zorder, self.show_time, self.animation_time, self.displayable))
-
- def __getitem__(self, index):
- return (self.tag, self.zorder, self.show_time, self.animation_time, self.displayable)[index]
-
- def __repr__(self):
- return "<SLE: %r %r %r>" % (self.tag, self.name, self.displayable)
-
- def copy(self):
- return SceneListEntry(
- self.tag,
- self.zorder,
- self.show_time,
- self.animation_time,
- self.displayable,
- self.name)
-
- def update_time(self, time):
-
- rv = self
-
- if self.show_time is None or self.animation_time is None:
- rv = self.copy()
- rv.show_time = rv.show_time or time
- rv.animation_time = rv.animation_time or time
-
- return rv
-
-
-class SceneLists(renpy.object.Object):
- """
- This stores the current scene lists that are being used to display
- things to the user.
- """
-
- __version__ = 6
-
- def after_setstate(self):
- for i in renpy.config.layers + renpy.config.top_layers:
- if i not in self.layers:
- self.layers[i] = [ ]
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- def after_upgrade(self, version):
-
- if version < 1:
-
- self.at_list = { }
- self.layer_at_list = { }
-
- for i in renpy.config.layers + renpy.config.top_layers:
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- if version < 3:
- self.shown_window = False
-
- if version < 4:
- for k in self.layers:
- self.layers[k] = [ SceneListEntry(*(i + (None,)) ) for i in self.layers[k] ]
-
- self.additional_transient = [ ]
-
- if version < 5:
- self.drag_group = None
-
- if version < 6:
- self.shown = self.image_predict_info
-
- def __init__(self, oldsl, shown):
-
- super(SceneLists, self).__init__()
-
- # Has a window been shown as part of these scene lists?
- self.shown_window = False
-
- # A map from layer name -> list(SceneListEntry)
- self.layers = { }
-
- # A map from layer name -> tag -> at_list associated with that tag.
- self.at_list = { }
-
- # A map from layer to (star time, at_list), where the at list has
- # been applied to the layer as a whole.
- self.layer_at_list = { }
-
- # The current shown images,
- self.shown = shown
-
- # A list of (layer, tag) pairs that are considered to be
- # transient.
- self.additional_transient = [ ]
-
- # Either None, or a DragGroup that's used as the default for
- # drags with names.
- self.drag_group = None
-
- if oldsl:
-
- for i in renpy.config.layers + renpy.config.top_layers:
-
- try:
- self.layers[i] = oldsl.layers[i][:]
- except KeyError:
- self.layers[i] = [ ]
-
- if i in oldsl.at_list:
- self.at_list[i] = oldsl.at_list[i].copy()
- self.layer_at_list[i] = oldsl.layer_at_list[i]
- else:
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- for i in renpy.config.overlay_layers:
- self.clear(i)
-
- self.replace_transient()
-
- self.focused = None
-
- self.drag_group = oldsl.drag_group
-
- else:
- for i in renpy.config.layers + renpy.config.top_layers:
- self.layers[i] = [ ]
- self.at_list[i] = { }
- self.layer_at_list[i] = (None, [ ])
-
- self.music = None
- self.focused = None
-
- def replace_transient(self):
- """
- Replaces the contents of the transient display list with
- a copy of the master display list. This is used after a
- scene is displayed to get rid of transitions and interface
- elements.
- """
-
- for i in renpy.config.transient_layers:
- self.clear(i, True)
-
- for layer, tag in self.additional_transient:
- self.remove(layer, tag)
-
- self.additional_transient = [ ]
-
- def transient_is_empty(self):
- """
- This returns True if all transient layers are empty. This is
- used by the rollback code, as we can't start a new rollback
- if there is something in a transient layer (as things in the
- transient layer may contain objects that cannot be pickled,
- like lambdas.)
- """
-
- for i in renpy.config.transient_layers:
- if self.layers[i]:
- return False
-
- return True
-
- def transform_state(self, old_thing, new_thing):
- """
- If the old thing is a transform, then move the state of that transform
- to the new thing.
- """
-
- if old_thing is None:
- return new_thing
-
- # Don't bother wrapping screens, as they can't be transformed.
- if isinstance(new_thing, renpy.display.screen.ScreenDisplayable):
- return new_thing
-
- old_transform = old_thing._get_parameterized()
- if not isinstance(old_transform, renpy.display.motion.Transform):
- return new_thing
-
- new_transform = new_thing._get_parameterized()
- if not isinstance(new_transform, renpy.display.motion.Transform):
- new_thing = new_transform = renpy.display.motion.Transform(child=new_thing)
-
- new_transform.take_state(old_transform)
- return new_thing
-
-
- def find_index(self, layer, tag, zorder, behind):
- """
- This finds the spot in the named layer where we should insert the
- displayable. It returns two things: an index at which the new thing
- should be added, and an index at which the old thing should be hidden.
- (Note that the indexes are relative to the current state of the list,
- which may change on an add or remove.)
- """
-
- add_index = None
- remove_index = None
-
-
- for i, sle in enumerate(self.layers[layer]):
-
- if add_index is None:
-
- if sle.zorder == zorder:
- if sle.tag and (sle.tag == tag or sle.tag in behind):
- add_index = i
-
- elif sle.zorder > zorder:
- add_index = i
-
-
- if remove_index is None:
- if (sle.tag and sle.tag == tag) or sle.displayable == tag:
- remove_index = i
-
-
- if add_index is None:
- add_index = len(self.layers[layer])
-
- return add_index, remove_index
-
-
- def add(self,
- layer,
- thing,
- key=None,
- zorder=0,
- behind=[ ],
- at_list=[ ],
- name=None,
- atl=None,
- default_transform=None,
- transient=False):
- """
- Adds something to this scene list. Some of these names are quite a bit
- out of date.
-
- `thing` - The displayable to add.
-
- `key` - A string giving the tag associated with this thing.
-
- `zorder` - Where to place this thing in the zorder, an integer
- A greater value means closer to the user.
-
- `behind` - A list of tags to place the thing behind.
-
- `at_list` - The at_list associated with this
- displayable. Counterintunitively, this is not actually
- applied, but merely stored for future use.
-
- `name` - The full name of the image being displayed. This is used for
- image lookup.
-
- `atl` - If not None, an atl block applied to the thing. (This actually is
- applied here.)
-
- `default_transform` - The default transform that is used to initialized
- the values in the other transforms.
- """
-
- if not isinstance(thing, Displayable):
- raise Exception("Attempting to show something that isn't a displayable:" + repr(thing))
-
- if layer not in self.layers:
- raise Exception("Trying to add something to non-existent layer '%s'." % layer)
-
- if key:
- self.remove_hide_replaced(layer, key)
- self.at_list[layer][key] = at_list
-
- if key and name:
- self.shown.predict_show(layer, name)
-
- if transient:
- self.additional_transient.append((layer, key))
-
- l = self.layers[layer]
-
- if atl:
- thing = renpy.display.motion.ATLTransform(atl, child=thing)
-
- add_index, remove_index = self.find_index(layer, key, zorder, behind)
-
- at = None
- st = None
-
- if remove_index is not None:
- sle = l[remove_index]
- at = sle.animation_time
- old = sle.displayable
-
- if (not atl and
- not at_list and
- renpy.config.keep_running_transform and
- isinstance(old, renpy.display.motion.Transform)):
-
- thing = sle.displayable._change_transform_child(thing)
- else:
- thing = self.transform_state(l[remove_index].displayable, thing)
-
- thing.set_transform_event("replace")
- thing._show()
-
- else:
-
- if not isinstance(thing, renpy.display.motion.Transform):
- thing = self.transform_state(default_transform, thing)
-
- thing.set_transform_event("show")
- thing._show()
-
- sle = SceneListEntry(key, zorder, st, at, thing, name)
- l.insert(add_index, sle)
-
- if remove_index is not None:
- if add_index <= remove_index:
- remove_index += 1
-
- self.hide_or_replace(layer, remove_index, "replaced")
-
- def hide_or_replace(self, layer, index, prefix):
- """
- Hides or replaces the scene list entry at the given
- index. `prefix` is a prefix that is used if the entry
- decides it doesn't want to be hidden quite yet.
- """
-
- if index is None:
- return
-
- l = self.layers[layer]
- oldsle = l[index]
-
- now = get_time()
-
- st = oldsle.show_time or now
- at = oldsle.animation_time or now
-
- if oldsle.tag:
-
- d = oldsle.displayable._hide(now - st, now - at, prefix)
-
- # _hide can mutate the layers, so we need to recompute
- # index.
- index = l.index(oldsle)
-
- if d is not None:
-
- sle = SceneListEntry(
- prefix + "$" + oldsle.tag,
- oldsle.zorder,
- st,
- at,
- d,
- None)
-
- l[index] = sle
-
- return
-
- l.pop(index)
-
- def get_all_displayables(self):
- """
- Gets all displayables reachable from this scene list.
- """
-
- rv = [ ]
- for l in self.layers.values():
- for sle in l:
- rv.append(sle.displayable)
-
- return rv
-
- def remove_above(self, layer, thing):
- """
- Removes everything on the layer that is closer to the user
- than thing, which may be either a tag or a displayable. Thing must
- be displayed, or everything will be removed.
- """
-
- for i in reversed(range(len(self.layers[layer]))):
-
- sle = self.layers[layer][i]
-
- if thing:
- if sle.tag == thing or sle.displayable == thing:
- break
-
- if sle.tag and "$" in sle.tag:
- continue
-
- self.hide_or_replace(layer, i, "hide")
-
- def remove(self, layer, thing):
- """
- Thing is either a key or a displayable. This iterates through the
- named layer, searching for entries matching the thing.
- When they are found, they are removed from the displaylist.
-
- It's not an error to remove something that isn't in the layer in
- the first place.
- """
-
- if layer not in self.layers:
- raise Exception("Trying to remove something from non-existent layer '%s'." % layer)
-
- _add_index, remove_index = self.find_index(layer, thing, 0, [ ])
-
- if remove_index is not None:
- tag = self.layers[layer][remove_index].tag
-
- if tag:
- self.shown.predict_hide(layer, (tag,))
- self.at_list[layer].pop(tag, None)
-
- self.hide_or_replace(layer, remove_index, "hide")
-
- def clear(self, layer, hide=False):
- """
- Clears the named layer, making it empty.
-
- If hide is True, then objects are hidden. Otherwise, they are
- totally wiped out.
- """
-
- if not hide:
- self.layers[layer] = [ ]
-
- else:
-
- # Have to iterate in reverse order, since otherwise
- # the indexes might change.
- for i in reversed(range(len(self.layers[layer]))):
- self.hide_or_replace(layer, i, hide)
-
- self.at_list[layer].clear()
- self.shown.predict_scene(layer)
- self.layer_at_list[layer] = (None, [ ])
-
- def set_layer_at_list(self, layer, at_list):
- self.layer_at_list[layer] = (None, list(at_list))
-
- def set_times(self, time):
- """
- This finds entries with a time of None, and replaces that
- time with the given time.
- """
-
- for l, (t, list) in list(self.layer_at_list.items()): #@ReservedAssignment
- self.layer_at_list[l] = (t or time, list)
-
- for l, ll in self.layers.items():
- self.layers[l] = [ i.update_time(time) for i in ll ]
-
- def showing(self, layer, name):
- """
- Returns true if something with the prefix of the given name
- is found in the scene list.
- """
-
- return self.shown.showing(layer, name)
-
- def make_layer(self, layer, properties):
- """
- Creates a Fixed with the given layer name and scene_list.
- """
-
- rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **properties)
- rv.append_scene_list(self.layers[layer])
-
- time, at_list = self.layer_at_list[layer]
-
- if at_list:
- for a in at_list:
-
- if isinstance(a, renpy.display.motion.Transform):
- rv = a(child=rv)
- else:
- rv = a(rv)
-
- f = renpy.display.layout.MultiBox(layout='fixed')
- f.add(rv, time, time)
- rv = f
-
- rv.layer_name = layer
- return rv
-
- def remove_hide_replaced(self, layer, tag):
- """
- Removes things that are hiding or replaced, that have the given
- tag.
- """
-
- hide_tag = "hide$" + tag
- replaced_tag = "replaced$" + tag
-
- l = self.layers[layer]
- self.layers[layer] = [ i for i in l if i.tag != hide_tag and i.tag != replaced_tag ]
-
- def remove_hidden(self):
- """
- Goes through all of the layers, and removes things that are
- hidden and are no longer being kept alive by their hide
- methods.
- """
-
- now = get_time()
-
- for l in self.layers:
- newl = [ ]
-
- for sle in self.layers[l]:
-
- if sle.tag:
-
- if sle.tag.startswith("hide$"):
- d = sle.displayable._hide(now - sle.show_time, now - sle.animation_time, "hide")
- if not d:
- continue
-
- elif sle.tag.startswith("replaced$"):
- d = sle.displayable._hide(now - sle.show_time, now - sle.animation_time, "replaced")
- if not d:
- continue
-
- newl.append(sle)
-
- self.layers[l] = newl
-
- def get_displayable_by_tag(self, layer, tag):
- """
- Returns the displayable on the layer with the given tag, or None
- if no such displayable exists. Note that this will usually return
- a Transform.
- """
-
- if layer not in self.layers:
- raise Exception("Unknown layer %r." % layer)
-
- for sle in self.layers[layer]:
- if sle.tag == tag:
- return sle.displayable
-
- return None
-
- def get_displayable_by_name(self, layer, name):
- """
- Returns the displayable on the layer with the given tag, or None
- if no such displayable exists. Note that this will usually return
- a Transform.
- """
-
- if layer not in self.layers:
- raise Exception("Unknown layer %r." % layer)
-
- for sle in self.layers[layer]:
-
- if sle.name == name:
- return sle.displayable
-
- return None
-
-
-def scene_lists(index=-1):
- """
- Returns either the current scenelists object, or the one for the
- context at the given index.
- """
-
- return renpy.game.context(index).scene_lists
-
-
-class Interface(object):
- """
- This represents the user interface that interacts with the user.
- It manages the Display objects that display things to the user, and
- also handles accepting and responding to user input.
-
- @ivar display: The display that we used to display the screen.
-
- @ivar profile_time: The time of the last profiling.
-
- @ivar screenshot: A screenshot, or None if no screenshot has been
- taken.
-
- @ivar old_scene: The last thing that was displayed to the screen.
-
- @ivar transition: A map from layer name to the transition that will
- be applied the next time interact restarts.
-
- @ivar transition_time: A map from layer name to the time the transition
- involving that layer started.
-
- @ivar transition_from: A map from layer name to the scene that we're
- transitioning from on that layer.
-
- @ivar suppress_transition: If True, then the next transition will not
- happen.
-
- @ivar force_redraw: If True, a redraw is forced.
-
- @ivar restart_interaction: If True, the current interaction will
- be restarted.
-
- @ivar pushed_event: If not None, an event that was pushed back
- onto the stack.
-
- @ivar mouse: The name of the mouse cursor to use during the current
- interaction.
-
- @ivar ticks: The number of 20hz ticks.
-
- @ivar frame_time: The time at which we began drawing this frame.
-
- @ivar interact_time: The time of the start of the first frame of the current interact_core.
-
- @ivar time_event: A singleton ignored event.
-
- @ivar event_time: The time of the current event.
-
- @ivar timeout_time: The time at which the timeout will occur.
- """
-
- def __init__(self):
- self.screenshot = None
- self.old_scene = { }
- self.transition = { }
- self.ongoing_transition = { }
- self.transition_time = { }
- self.transition_from = { }
- self.suppress_transition = False
- self.quick_quit = False
- self.force_redraw = False
- self.restart_interaction = False
- self.pushed_event = None
- self.ticks = 0
- self.mouse = 'default'
- self.timeout_time = None
- self.last_event = None
- self.current_context = None
- self.roll_forward = None
-
- # Things to be preloaded.
- self.preloads = [ ]
-
- # The time at which this draw occurs.
- self.frame_time = 0
-
- # The time when this interaction occured.
- self.interact_time = None
-
- # The time we last tried to quit.
- self.quit_time = 0
-
- self.time_event = pygame.event.Event(TIMEEVENT)
- self.redraw_event = pygame.event.Event(REDRAW)
-
- # Are we focused?
- self.focused = True
-
- # Properties for each layer.
- self.layer_properties = { }
-
- # Have we shown the window this interaction?
- self.shown_window = False
-
- # Are we in fullscren mode?
- self.fullscreen = False
-
- for layer in renpy.config.layers + renpy.config.top_layers:
- if layer in renpy.config.layer_clipping:
- x, y, w, h = renpy.config.layer_clipping[layer]
- self.layer_properties[layer] = dict(
- xpos = x,
- xanchor = 0,
- ypos = y,
- yanchor = 0,
- xmaximum = w,
- ymaximum = h,
- xminimum = w,
- yminimum = h,
- clipping = True,
- )
-
- else:
- self.layer_properties[layer] = dict()
-
-
- # A stack giving the values of self.transition and self.transition_time
- # for contexts outside the current one. This is used to restore those
- # in the case where nothing has changed in the new context.
- self.transition_info_stack = [ ]
-
- # The time when the event was dispatched.
- self.event_time = 0
-
- # The time we saw the last mouse event.
- self.mouse_event_time = None
-
- # Should we show the mouse?
- self.show_mouse = True
-
- # Should we reset the display?
- self.display_reset = False
-
- # The last size we were resized to. This lets us debounce the
- # VIDEORESIZE event.
- self.last_resize = None
-
- # Ensure that we kill off the presplash.
- renpy.display.presplash.end()
-
- # Initialize pygame.
- if pygame.version.vernum < (1, 8, 1):
- raise Exception("Ren'Py requires pygame 1.8.1 to run.")
-
- try:
- import pygame.macosx as macosx
- macosx.init() #@UndefinedVariable
- except:
- pass
-
- try:
- macosx.Video_AutoInit() #@UndefinedVariable
- except:
- pass
-
- pygame.font.init()
- renpy.audio.audio.init()
- renpy.display.joystick.init()
- pygame.display.init()
-
- # Init timing.
- init_time()
- self.profile_time = get_time()
- self.mouse_event_time = get_time()
-
- # The current window caption.
- self.window_caption = None
-
- renpy.game.interface = self
- renpy.display.interface = self
-
- # Are we in safe mode, from holding down shift at start?
- self.safe_mode = False
- if renpy.first_utter_start and (pygame.key.get_mods() & pygame.KMOD_SHIFT):
- self.safe_mode = True
-
- # Setup the video mode.
- self.set_mode()
-
- # Load the image fonts.
- renpy.text.font.load_image_fonts()
-
- # Setup the android keymap.
- if android is not None:
- android.map_key(android.KEYCODE_BACK, pygame.K_PAGEUP)
- android.map_key(android.KEYCODE_MENU, pygame.K_ESCAPE)
-
- # Double check, since at least on Linux, we can't set safe_mode until
- # the window maps.
- if renpy.first_utter_start and (pygame.key.get_mods() & pygame.KMOD_SHIFT):
- self.safe_mode = True
-
- # Setup periodic event.
- pygame.time.set_timer(PERIODIC, PERIODIC_INTERVAL)
-
- # Don't grab the screen.
- pygame.event.set_grab(False)
-
- # Do we need a background screenshot?
- self.bgscreenshot_needed = False
-
- # Event used to signal background screenshot taken.
- self.bgscreenshot_event = threading.Event()
-
- # The background screenshot surface.
- self.bgscreenshot_surface = None
-
-
- def post_init(self):
- # Setup.
-
- # Needed for Unity.
- wmclass = renpy.config.save_directory or os.path.basename(sys.argv[0])
- os.environ['SDL_VIDEO_X11_WMCLASS'] = wmclass
-
- self.set_window_caption(force=True)
- self.set_icon()
-
- if renpy.config.key_repeat is not None:
- delay, repeat_delay = renpy.config.key_repeat
- pygame.key.set_repeat(int(1000 * delay), int(1000 * repeat_delay))
-
- def set_icon(self):
- """
- This is called to set up the window icon.
- """
-
- # Window icon.
- icon = renpy.config.window_icon
-
- if renpy.windows and renpy.config.windows_icon:
- icon = renpy.config.windows_icon
-
- if icon:
-
- im = renpy.display.scale.image_load_unscaled(
- renpy.loader.load(icon),
- icon,
- convert=False,
- )
-
- # Convert the aspect ratio to be square.
- iw, ih = im.get_size()
- imax = max(iw, ih)
- square_im = renpy.display.pgrender.surface_unscaled((imax, imax), True)
- square_im.blit(im, ( (imax-iw)/2, (imax-ih)/2 ))
- im = square_im
-
- if renpy.windows and im.get_size() != (32, 32):
- im = renpy.display.scale.real_smoothscale(im, (32, 32))
-
- pygame.display.set_icon(im)
-
-
- def set_window_caption(self, force=False):
- caption = renpy.config.window_title + renpy.store._window_subtitle
- if not force and caption == self.window_caption:
- return
-
- self.window_caption = caption
- pygame.display.set_caption(caption.encode("utf-8"))
-
- def iconify(self):
- pygame.display.iconify()
-
- def get_draw_constructors(self):
- """
- Figures out the list of draw constructors to try.
- """
-
- renderer = renpy.game.preferences.renderer
- renderer = os.environ.get("RENPY_RENDERER", renderer)
-
- if self.safe_mode:
- renderer = "sw"
-
- renpy.config.renderer = renderer
-
- if renderer == "auto":
- if renpy.windows:
- renderers = [ "gl", "angle", "sw" ]
- else:
- renderers = [ "gl", "sw" ]
- else:
- renderers = [ renderer, "sw" ]
-
- draw_objects = { }
-
- def make_draw(name, mod, cls, *args):
- if name not in renderers:
- return False
-
- try:
- __import__(mod)
- module = sys.modules[mod]
- draw_class = getattr(module, cls)
- draw_objects[name] = draw_class(*args)
- return True
-
- except:
- renpy.display.log.write("Couldn't import {0} renderer:".format(name))
- renpy.display.log.exception()
-
- return False
-
- if renpy.windows:
- has_angle = make_draw("angle", "renpy.angle.gldraw", "GLDraw")
- else:
- has_angle = False
-
- make_draw("gl", "renpy.gl.gldraw", "GLDraw", not has_angle)
- make_draw("sw", "renpy.display.swdraw", "SWDraw")
-
- rv = [ ]
-
- def append_draw(name):
- if name in draw_objects:
- rv.append(draw_objects[name])
- else:
- renpy.display.log.write("Unknown renderer: {0}".format(name))
-
- for i in renderers:
- append_draw(i)
-
- return rv
-
-
- def kill_textures(self):
- renpy.display.render.free_memory()
- renpy.text.text.layout_cache_clear()
-
- def kill_textures_and_surfaces(self):
- """
- Kill all textures and surfaces that are loaded.
- """
-
- self.kill_textures()
-
- renpy.display.im.cache.clear()
- renpy.display.module.bo_cache = None
-
- def set_mode(self, physical_size=None):
- """
- This sets the video mode. It also picks the draw object.
- """
-
- # Ensure that we kill off the movie when changing screen res.
- if renpy.display.draw and renpy.display.draw.info["renderer"] == "sw":
- renpy.display.video.movie_stop(clear=False)
-
- if self.display_reset:
- renpy.display.draw.deinit()
-
- if renpy.display.draw.info["renderer"] == "angle":
- renpy.display.draw.quit()
-
- renpy.display.render.free_memory()
- renpy.display.im.cache.clear()
- renpy.text.text.layout_cache_clear()
-
- renpy.display.module.bo_cache = None
-
- self.kill_textures_and_surfaces()
-
- self.display_reset = False
-
- virtual_size = (renpy.config.screen_width, renpy.config.screen_height)
-
- if physical_size is None:
- if renpy.android or renpy.game.preferences.physical_size is None: #@UndefinedVariable
- physical_size = (renpy.config.screen_width, renpy.config.screen_height)
- else:
- physical_size = renpy.game.preferences.physical_size
-
- # Setup screen.
- fullscreen = renpy.game.preferences.fullscreen
-
- # If we're in fullscreen mode, and changing to another mode, go to
- # windowed mode first.
- s = pygame.display.get_surface()
- if s and (s.get_flags() & pygame.FULLSCREEN):
- fullscreen = False
-
- old_fullscreen = self.fullscreen
- self.fullscreen = fullscreen
-
- if os.environ.get('RENPY_DISABLE_FULLSCREEN', False):
- fullscreen = False
- self.fullscreen = renpy.game.preferences.fullscreen
-
- if renpy.display.draw:
- draws = [ renpy.display.draw ]
- else:
- draws = self.get_draw_constructors()
-
- for draw in draws:
- if draw.set_mode(virtual_size, physical_size, fullscreen):
- renpy.display.draw = draw
- break
- else:
- pygame.display.quit()
- else:
- # Ensure we don't get stuck in fullscreen.
- renpy.game.preferences.fullscreen = False
- raise Exception("Could not set video mode.")
-
- # Save the video size.
- if renpy.config.save_physical_size and not fullscreen and not old_fullscreen:
- renpy.game.preferences.physical_size = renpy.display.draw.get_physical_size()
-
- if android:
- android.init()
-
- # We need to redraw the (now blank) screen.
- self.force_redraw = True
-
- # Assume we have focus until told otherwise.
- self.focused = True
-
- # Assume we're not minimized.
- self.minimized = False
-
- # Force an interaction restart.
- self.restart_interaction = True
-
-
- def draw_screen(self, root_widget, fullscreen_video, draw):
-
- surftree = renpy.display.render.render_screen(
- root_widget,
- renpy.config.screen_width,
- renpy.config.screen_height,
- )
-
- if draw:
- renpy.display.draw.draw_screen(surftree, fullscreen_video)
-
- renpy.display.render.mark_sweep()
- renpy.display.focus.take_focuses()
-
- self.surftree = surftree
- self.fullscreen_video = fullscreen_video
-
-
- def take_screenshot(self, scale, background=False):
- """
- This takes a screenshot of the current screen, and stores it so
- that it can gotten using get_screenshot()
-
- `background`
- If true, we're in a background thread. So queue the request
- until it can be handled by the main thread.
- """
-
- if background:
- self.bgscreenshot_event.clear()
- self.bgscreenshot_needed = True
- self.bgscreenshot_event.wait()
-
- window = self.bgscreenshot_surface
- self.bgscreenshot_surface = None
-
- else:
-
- window = renpy.display.draw.screenshot(self.surftree, self.fullscreen_video)
-
- surf = renpy.display.pgrender.copy_surface(window, True)
- surf = renpy.display.scale.smoothscale(surf, scale)
- surf = surf.convert()
-
- sio = io.StringIO()
- renpy.display.module.save_png(surf, sio, 0)
- self.screenshot = sio.getvalue()
- sio.close()
-
-
- def save_screenshot(self, filename):
- """
- Saves a full-size screenshot in the given filename.
- """
-
- window = renpy.display.draw.screenshot(self.surftree, self.fullscreen_video)
-
- if renpy.config.screenshot_crop:
- window = window.subsurface(renpy.config.screenshot_crop)
-
- try:
- renpy.display.scale.image_save_unscaled(window, filename)
- except:
- if renpy.config.debug:
- raise
- pass
-
-
- def get_screenshot(self):
- """
- Gets the current screenshot, as a string. Returns None if there isn't
- a current screenshot.
- """
-
- rv = self.screenshot
-
- if not rv:
- self.take_screenshot((renpy.config.thumbnail_width, renpy.config.thumbnail_height))
- rv = self.screenshot
- self.lose_screenshot()
-
- return rv
-
-
- def lose_screenshot(self):
- """
- This deallocates the saved screenshot.
- """
-
- self.screenshot = None
-
-
- def show_window(self):
-
- if not renpy.store._window:
- return
-
- if renpy.game.context().scene_lists.shown_window:
- return
-
- if renpy.config.empty_window:
- renpy.config.empty_window()
-
- def do_with(self, trans, paired, clear=False):
-
- if renpy.config.with_callback:
- trans = renpy.config.with_callback(trans, paired)
-
- if (not trans) or self.suppress_transition:
- self.with_none()
- return False
- else:
- self.set_transition(trans)
- return self.interact(trans_pause=True,
- suppress_overlay=not renpy.config.overlay_during_with,
- mouse='with',
- clear=clear)
-
- def with_none(self):
- """
- Implements the with None command, which sets the scene we will
- be transitioning from.
- """
-
- renpy.exports.say_attributes = None
-
- # Show the window, if that's necessary.
- self.show_window()
-
- # Compute the overlay.
- self.compute_overlay()
-
- scene_lists = renpy.game.context().scene_lists
-
- # Compute the scene.
- self.old_scene = self.compute_scene(scene_lists)
-
- # Get rid of transient things.
-
- for i in renpy.config.overlay_layers:
- scene_lists.clear(i)
-
- scene_lists.replace_transient()
- scene_lists.shown_window = False
-
-
- def set_transition(self, transition, layer=None, force=False):
- """
- Sets the transition that will be performed as part of the next
- interaction.
- """
-
- if self.suppress_transition and not force:
- return
-
- if transition is None:
- self.transition.pop(layer, None)
- else:
- self.transition[layer] = transition
-
-
- def event_peek(self):
- """
- This peeks the next event. It returns None if no event exists.
- """
-
- if self.pushed_event:
- return self.pushed_event
-
- ev = pygame.event.poll()
-
- if ev.type == pygame.NOEVENT:
- # Seems to prevent the CPU from speeding up.
- renpy.display.draw.event_peek_sleep()
- return None
-
- self.pushed_event = ev
-
- return ev
-
- def event_poll(self):
- """
- Called to busy-wait for an event while we're waiting to
- redraw a frame.
- """
-
- if self.pushed_event:
- rv = self.pushed_event
- self.pushed_event = None
- else:
- rv = pygame.event.poll()
-
- self.last_event = rv
-
- return rv
-
-
- def event_wait(self):
- """
- This is in its own function so that we can track in the
- profiler how much time is spent in interact.
- """
-
- if self.pushed_event:
- rv = self.pushed_event
- self.pushed_event = None
- self.last_event = rv
- return rv
-
- # Handle a request for a background screenshot.
- if self.bgscreenshot_needed:
- self.bgscreenshot_needed = False
- self.bgscreenshot_surface = renpy.display.draw.screenshot(self.surftree, self.fullscreen_video)
- self.bgscreenshot_event.set()
-
- try:
- cpu_idle.set()
- ev = pygame.event.wait()
- finally:
- cpu_idle.clear()
-
- self.last_event = ev
-
- return ev
-
-
- def compute_overlay(self):
-
- if renpy.store.suppress_overlay:
- return
-
- # Figure out what the overlay layer should look like.
- renpy.ui.layer("overlay")
-
- for i in renpy.config.overlay_functions:
- i()
-
- if renpy.game.context().scene_lists.shown_window:
- for i in renpy.config.window_overlay_functions:
- i()
-
- renpy.ui.close()
-
-
- def compute_scene(self, scene_lists):
- """
- This converts scene lists into a dictionary mapping layer
- name to a Fixed containing that layer.
- """
-
- rv = { }
-
- for layer in renpy.config.layers + renpy.config.top_layers:
- rv[layer] = scene_lists.make_layer(layer, self.layer_properties[layer])
-
- root = renpy.display.layout.MultiBox(layout='fixed')
- root.layers = { }
-
- for layer in renpy.config.layers:
- root.layers[layer] = rv[layer]
- root.add(rv[layer])
- rv[None] = root
-
- return rv
-
-
- def quit_event(self):
- """
- This is called to handle the user invoking a quit.
- """
-
- if self.quit_time > (time.time() - .75):
- raise renpy.game.QuitException()
-
- if renpy.config.quit_action is not None:
- self.quit_time = time.time()
-
- # Make the screen more suitable for interactions.
- renpy.exports.movie_stop(only_fullscreen=True)
- renpy.store.mouse_visible = True
-
- renpy.display.behavior.run(renpy.config.quit_action)
- else:
- raise renpy.game.QuitException()
-
-
- def get_mouse_info(self):
- # Figure out if the mouse visibility algorithm is hiding the mouse.
- if self.mouse_event_time + renpy.config.mouse_hide_time < renpy.display.core.get_time():
- visible = False
- else:
- visible = renpy.store.mouse_visible and (not renpy.game.less_mouse)
-
- visible = visible and self.show_mouse
-
- # If not visible, hide the mouse.
- if not visible:
- return False, 0, 0, None
-
- # Deal with a hardware mouse, the easy way.
- if not renpy.config.mouse:
- return True, 0, 0, None
-
- # Deal with the mouse going offscreen.
- if not self.focused:
- return False, 0, 0, None
-
- mouse_kind = renpy.display.focus.get_mouse() or self.mouse
-
- # Figure out the mouse animation.
- if mouse_kind in renpy.config.mouse:
- anim = renpy.config.mouse[mouse_kind]
- else:
- anim = renpy.config.mouse[getattr(renpy.store, 'default_mouse', 'default')]
-
- img, x, y = anim[self.ticks % len(anim)]
- tex = renpy.display.im.load_image(img)
-
- return False, x, y, tex
-
- def drawn_since(self, seconds_ago):
- """
- Returns true if the screen has been drawn in the last `seconds_ago`,
- and false otherwise.
- """
-
- return (get_time() - self.frame_time) <= seconds_ago
-
- def android_check_suspend(self):
-
- if android.check_pause():
-
- android.sound.pause_all()
-
- pygame.time.set_timer(PERIODIC, 0)
- pygame.time.set_timer(REDRAW, 0)
- pygame.time.set_timer(TIMEEVENT, 0)
-
- # The game has to be saved.
- renpy.loadsave.save("_reload-1")
-
- android.wait_for_resume()
-
- # Since we came back to life, we can get rid of the
- # auto-reload.
- renpy.loadsave.unlink_save("_reload-1")
-
- pygame.time.set_timer(PERIODIC, PERIODIC_INTERVAL)
-
- android.sound.unpause_all()
-
- def iconified(self):
- """
- Called when we become an icon.
- """
-
- if self.minimized:
- return
-
- self.minimized = True
-
- renpy.display.log.write("The window was minimized.")
-
-
- def restored(self):
- """
- Called when we are restored from being an icon.
- """
-
- # This is necessary on Windows/DirectX/Angle, as otherwise we get
- # a blank screen.
-
- if not self.minimized:
- return
-
- self.minimized = False
-
- renpy.display.log.write("The window was restored.")
-
- if renpy.windows:
- self.display_reset = True
- self.set_mode(self.last_resize)
-
- def enter_context(self):
- """
- Called when we enter a new context.
- """
-
- # Stop ongoing transitions.
- self.ongoing_transition.clear()
- self.transition_from.clear()
- self.transition_time.clear()
-
- def post_time_event(self):
- """
- Posts a time_event object to the queue.
- """
-
- try:
- pygame.event.post(self.time_event)
- except:
- pass
-
- def interact(self, clear=True, suppress_window=False, **kwargs):
- """
- This handles an interaction, restarting it if necessary. All of the
- keyword arguments are passed off to interact_core.
- """
-
- # Cancel magic error reporting.
- renpy.bootstrap.report_error = None
-
- context = renpy.game.context()
-
- if context.interacting:
- raise Exception("Cannot start an interaction in the middle of an interaction, without creating a new context.")
-
- context.interacting = True
-
-
- # Show a missing window.
- if not suppress_window:
- self.show_window()
-
- # These things can be done once per interaction.
-
- preloads = self.preloads
- self.preloads = [ ]
-
- try:
- renpy.game.after_rollback = False
-
- for i in renpy.config.start_interact_callbacks:
- i()
-
- repeat = True
-
- while repeat:
- repeat, rv = self.interact_core(preloads=preloads, **kwargs)
-
- return rv
-
- finally:
-
- context.interacting = False
-
- # Clean out transient stuff at the end of an interaction.
- if clear:
- scene_lists = renpy.game.context().scene_lists
- scene_lists.replace_transient()
-
- self.ongoing_transition = { }
- self.transition_time = { }
- self.transition_from = { }
-
- self.restart_interaction = True
-
- renpy.game.context().scene_lists.shown_window = False
-
- def interact_core(self,
- show_mouse=True,
- trans_pause=False,
- suppress_overlay=False,
- suppress_underlay=False,
- mouse='default',
- preloads=[],
- roll_forward=None,
- ):
-
- """
- This handles one cycle of displaying an image to the user,
- and then responding to user input.
-
- @param show_mouse: Should the mouse be shown during this
- interaction? Only advisory, and usually doesn't work.
-
- @param trans_pause: If given, we must have a transition. Should we
- add a pause behavior during the transition?
-
- @param suppress_overlay: This suppresses the display of the overlay.
- @param suppress_underlay: This suppresses the display of the underlay.
- """
-
- self.roll_forward = roll_forward
- self.show_mouse = show_mouse
-
- suppress_transition = renpy.config.skipping or renpy.game.less_updates
-
- # The global one.
- self.suppress_transition = False
-
- # Figure out transitions.
- for k in self.transition:
- if k not in self.old_scene:
- continue
-
- self.ongoing_transition[k] = self.transition[k]
- self.transition_from[k] = self.old_scene[k]
- self.transition_time[k] = None
-
- self.transition.clear()
-
- if suppress_transition:
- self.ongoing_transition.clear()
- self.transition_from.clear()
- self.transition_time.clear()
-
- ## Safety condition, prevents deadlocks.
- if trans_pause:
- if not self.ongoing_transition:
- return False, None
- if None not in self.ongoing_transition:
- return False, None
- if suppress_transition:
- return False, None
- if not self.old_scene:
- return False, None
-
- # Check to see if the language has changed.
- renpy.translation.check_language()
-
- # We just restarted.
- self.restart_interaction = False
-
- # Setup the mouse.
- self.mouse = mouse
-
- # The start and end times of this interaction.
- start_time = get_time()
- end_time = start_time
-
- # frames = 0
-
- for i in renpy.config.interact_callbacks:
- i()
-
- # Set the window caption.
- self.set_window_caption()
-
- # Tick time forward.
- renpy.display.im.cache.tick()
- renpy.text.text.layout_cache_tick()
- renpy.display.predict.reset()
-
- # Cleare the size groups.
- renpy.display.layout.size_groups.clear()
-
- # Clear some events.
- pygame.event.clear((pygame.MOUSEMOTION,
- PERIODIC,
- TIMEEVENT,
- REDRAW))
-
- # Add a single TIMEEVENT to the queue.
- self.post_time_event()
-
- # Figure out the scene list we want to show.
- scene_lists = renpy.game.context().scene_lists
-
- # Remove the now-hidden things.
- scene_lists.remove_hidden()
-
- # Compute the overlay.
- if not suppress_overlay:
- self.compute_overlay()
-
- # The root widget of everything that is displayed on the screen.
- root_widget = renpy.display.layout.MultiBox(layout='fixed')
- root_widget.layers = { }
-
- # A list of widgets that are roots of trees of widgets that are
- # considered for focusing.
- focus_roots = [ ]
-
- # Add the underlay to the root widget.
- if not suppress_underlay:
- for i in renpy.config.underlay:
- root_widget.add(i)
- focus_roots.append(i)
-
- if roll_forward is not None:
- rfw = renpy.display.behavior.RollForward(roll_forward)
- root_widget.add(rfw)
- focus_roots.append(rfw)
-
- # Figure out the scene. (All of the layers, and the root.)
- scene = self.compute_scene(scene_lists)
-
- # If necessary, load all images here.
- for w in scene.values():
- try:
- renpy.display.predict.displayable(w)
- except:
- pass
-
- # The root widget of all of the layers.
- layers_root = renpy.display.layout.MultiBox(layout='fixed')
- layers_root.layers = { }
-
- def add_layer(where, layer):
-
- scene_layer = scene[layer]
- focus_roots.append(scene_layer)
-
- if (self.ongoing_transition.get(layer, None) and
- not suppress_transition):
-
- trans = self.ongoing_transition[layer](
- old_widget=self.transition_from[layer],
- new_widget=scene_layer)
-
- if not isinstance(trans, Displayable):
- raise Exception("Expected transition to be a displayable, not a %r" % trans)
-
- transition_time = self.transition_time.get(layer, None)
-
- where.add(trans, transition_time, transition_time)
- where.layers[layer] = trans
-
- else:
- where.layers[layer] = scene_layer
- where.add(scene_layer)
-
- # Add layers (perhaps with transitions) to the layers root.
- for layer in renpy.config.layers:
- add_layer(layers_root, layer)
-
- # Add layers_root to root_widget, perhaps through a transition.
- if (self.ongoing_transition.get(None, None) and
- not suppress_transition):
-
- old_root = renpy.display.layout.MultiBox(layout='fixed')
- old_root.layers = { }
-
- for layer in renpy.config.layers:
- d = self.transition_from[None].layers[layer]
- old_root.layers[layer] = d
- old_root.add(d)
-
- trans = self.ongoing_transition[None](
- old_widget=old_root,
- new_widget=layers_root)
-
- if not isinstance(trans, Displayable):
- raise Exception("Expected transition to be a displayable, not a %r" % trans)
-
- trans._show()
-
- transition_time = self.transition_time.get(None, None)
- root_widget.add(trans, transition_time, transition_time)
-
- if trans_pause:
- sb = renpy.display.behavior.SayBehavior()
- root_widget.add(sb)
- focus_roots.append(sb)
-
- pb = renpy.display.behavior.PauseBehavior(trans.delay)
- root_widget.add(pb, transition_time, transition_time)
- focus_roots.append(pb)
-
- else:
- root_widget.add(layers_root)
-
- # Add top_layers to the root_widget.
- for layer in renpy.config.top_layers:
- add_layer(root_widget, layer)
-
- prediction_coroutine = renpy.display.predict.prediction_coroutine(root_widget)
- prediction_coroutine.send(None)
-
- # Clean out the registered adjustments.
- renpy.display.behavior.adj_registered.clear()
-
- # Clean up some movie-related things.
- renpy.display.video.early_interact()
-
- # Call per-interaction code for all widgets.
- root_widget.visit_all(lambda i : i.per_interact())
-
- # Now, update various things regarding scenes and transitions,
- # so we are ready for a new interaction or a restart.
- self.old_scene = scene
-
- # Okay, from here on we now have a single root widget (root_widget),
- # which we will try to show to the user.
-
- # Figure out what should be focused.
- renpy.display.focus.before_interact(focus_roots)
-
- # Redraw the screen.
- renpy.display.render.process_redraws()
- needs_redraw = True
-
- # First pass through the while loop?
- first_pass = True
-
- # We don't yet know when the interaction began.
- self.interact_time = None
-
- # We only want to do autosave once.
- did_autosave = False
-
- old_timeout_time = None
- old_redraw_time = None
-
- rv = None
-
- # Start sound.
- renpy.audio.audio.interact()
-
- # How long until we redraw.
- redraw_in = 3600
-
- # Have we drawn a frame yet?
- video_frame_drawn = False
-
- # This try block is used to force cleanup even on termination
- # caused by an exception propagating through this function.
- try:
-
- while rv is None:
-
- # Check for a change in fullscreen preference.
- if self.fullscreen != renpy.game.preferences.fullscreen or self.display_reset:
- self.set_mode()
- needs_redraw = True
-
- # Check for suspend.
- if android:
- self.android_check_suspend()
-
- # Redraw the screen.
- if (self.force_redraw or
- ((first_pass or not pygame.event.peek(ALL_EVENTS)) and
- renpy.display.draw.should_redraw(needs_redraw, first_pass))):
-
- self.force_redraw = False
-
- # If we have a movie, start showing it.
- fullscreen_video = renpy.display.video.interact()
-
- # Clean out the redraws, if we have to.
- # renpy.display.render.kill_redraws()
-
- # Draw the screen.
- self.frame_time = get_time()
-
- if not self.interact_time:
- self.interact_time = self.frame_time
-
- self.draw_screen(root_widget, fullscreen_video, (not fullscreen_video) or video_frame_drawn)
-
- if first_pass:
- scene_lists.set_times(self.interact_time)
- for k, v in self.transition_time.items():
- if v is None:
- self.transition_time[k] = self.interact_time
-
- renpy.config.frames += 1
-
- # If profiling is enabled, report the profile time.
- if renpy.config.profile :
- new_time = get_time()
-
- if new_time - self.profile_time > .015:
- print("Profile: Redraw took %f seconds." % (new_time - self.frame_time))
- print("Profile: %f seconds to complete event." % (new_time - self.profile_time))
-
- if first_pass and self.last_event:
- x, y = renpy.display.draw.get_mouse_pos()
- renpy.display.focus.mouse_handler(self.last_event, x, y, default=False)
-
- needs_redraw = False
- first_pass = False
-
- pygame.time.set_timer(REDRAW, 0)
- pygame.event.clear([REDRAW])
- old_redraw_time = None
-
- # Draw the mouse, if it needs drawing.
- renpy.display.draw.update_mouse()
-
- # See if we want to restart the interaction entirely.
- if self.restart_interaction:
- return True, None
-
- # Determine if we need a redraw. (We want to run these
- # functions, so we put them first to prevent short-circuiting.)
-
- if renpy.display.video.frequent():
- needs_redraw = True
- video_frame_drawn = True
-
- needs_redraw = renpy.display.video.frequent() or needs_redraw
- needs_redraw = renpy.display.render.process_redraws() or needs_redraw
-
- # How many seconds until we timeout.
- timeout_in = 3600
-
- # Handle the redraw timer.
- redraw_time = renpy.display.render.redraw_time()
-
- if (redraw_time is not None) and not needs_redraw:
- if redraw_time != old_redraw_time:
- time_left = redraw_time - get_time()
- time_left = min(time_left, 3600)
- redraw_in = time_left
-
- if time_left <= 0:
- try:
- pygame.event.post(self.redraw_event)
- except:
- pass
- pygame.time.set_timer(REDRAW, 0)
- else:
- pygame.time.set_timer(REDRAW, max(int(time_left * 1000), 1))
-
- old_redraw_time = redraw_time
- else:
- redraw_in = 3600
- pygame.time.set_timer(REDRAW, 0)
-
- # Handle the timeout timer.
- if not self.timeout_time:
- pygame.time.set_timer(TIMEEVENT, 0)
- else:
- time_left = self.timeout_time - get_time()
- time_left = min(time_left, 3600)
- timeout_in = time_left
-
- if time_left <= 0:
- self.timeout_time = None
- pygame.time.set_timer(TIMEEVENT, 0)
- self.post_time_event()
- elif self.timeout_time != old_timeout_time:
- # Always set to at least 1ms.
- pygame.time.set_timer(TIMEEVENT, int(time_left * 1000 + 1))
- old_timeout_time = self.timeout_time
-
- # Predict images, if we haven't done so already.
- while prediction_coroutine is not None:
-
- # Can we do expensive prediction?
- expensive_predict = not (needs_redraw or self.event_peek() or renpy.audio.music.is_playing("movie"))
-
- result = prediction_coroutine.send(expensive_predict)
-
- if not result:
- prediction_coroutine = None
- break
-
- if not expensive_predict:
- break
-
- # If we need to redraw again, do it if we don't have an
- # event going on.
- if needs_redraw and not self.event_peek():
- if renpy.config.profile:
- self.profile_time = get_time()
- continue
-
- # Handle autosaving, as necessary.
- if not did_autosave and not needs_redraw and not self.event_peek() and redraw_in > .25 and timeout_in > .25:
- renpy.loadsave.autosave()
- did_autosave = True
-
- if needs_redraw or renpy.display.video.playing():
- ev = self.event_poll()
- else:
- ev = self.event_wait()
-
- if ev.type == pygame.NOEVENT:
- continue
-
- if renpy.config.profile:
- self.profile_time = get_time()
-
- # Try to merge an TIMEEVENT with other timeevents.
- if ev.type == TIMEEVENT:
- old_timeout_time = None
- pygame.event.clear([TIMEEVENT])
-
- # On Android, where we have multiple mouse buttons, we can
- # merge a mouse down and mouse up event with its successor. This
- # prevents us from getting overwhelmed with too many events on
- # a multitouch screen.
- if android and (ev.type == pygame.MOUSEBUTTONDOWN or ev.type == pygame.MOUSEBUTTONUP):
- pygame.event.clear(ev.type)
-
- # Handle redraw timeouts.
- if ev.type == REDRAW:
- pygame.event.clear([REDRAW])
- old_redraw_time = None
- continue
-
- # Handle periodic events. This includes updating the mouse timers (and through the loop,
- # the mouse itself), and the audio system periodic calls.
- if ev.type == PERIODIC:
- events = 1 + len(pygame.event.get([PERIODIC]))
- self.ticks += events
-
- if renpy.config.periodic_callback:
- renpy.config.periodic_callback()
-
- renpy.audio.audio.periodic()
- continue
-
- # This can set the event to None, to ignore it.
- ev = renpy.display.joystick.event(ev)
- if not ev:
- continue
-
- # Handle skipping.
- renpy.display.behavior.skipping(ev)
-
- # Handle quit specially for now.
- if ev.type == pygame.QUIT:
- self.quit_event()
- continue
-
- # Handle videoresize.
- if ev.type == pygame.VIDEORESIZE:
- evs = pygame.event.get([pygame.VIDEORESIZE])
- if len(evs):
- ev = evs[-1]
-
- if self.last_resize != ev.size:
- self.last_resize = ev.size
- self.set_mode((ev.w, ev.h))
-
- continue
-
- if ev.type == pygame.MOUSEMOTION or \
- ev.type == pygame.MOUSEBUTTONDOWN or \
- ev.type == pygame.MOUSEBUTTONUP:
-
- self.mouse_event_time = renpy.display.core.get_time()
-
- # Merge mousemotion events.
- if ev.type == pygame.MOUSEMOTION:
- evs = pygame.event.get([pygame.MOUSEMOTION])
- if len(evs):
- ev = evs[-1]
-
- if renpy.windows:
- self.focused = True
-
- # Handle focus notifications.
- if ev.type == pygame.ACTIVEEVENT:
- if ev.state & 1:
- self.focused = ev.gain
-
- if ev.state & 4:
- if ev.gain:
- self.restored()
- else:
- self.iconified()
-
- pygame.key.set_mods(0)
-
- # This returns the event location. It also updates the
- # mouse state as necessary.
- x, y = renpy.display.draw.mouse_event(ev)
-
- if not self.focused:
- x = -1
- y = -1
-
- self.event_time = end_time = get_time()
-
- try:
-
- # Handle the event normally.
- rv = renpy.display.focus.mouse_handler(ev, x, y)
-
-
- if rv is None:
- rv = root_widget.event(ev, x, y, 0)
-
- if rv is None:
- rv = renpy.display.focus.key_handler(ev)
-
- if rv is not None:
- break
-
- # Handle displayable inspector.
- if renpy.config.inspector and renpy.display.behavior.inspector(ev):
- l = self.surftree.main_displayables_at_point(x, y, renpy.config.transient_layers + renpy.config.context_clear_layers + renpy.config.overlay_layers)
- renpy.game.invoke_in_new_context(renpy.config.inspector, l)
-
- except IgnoreEvent:
- # An ignored event can change the timeout. So we want to
- # process an TIMEEVENT to ensure that the timeout is
- # set correctly.
- self.post_time_event()
-
-
- # Check again after handling the event.
- needs_redraw |= renpy.display.render.process_redraws()
-
- if self.restart_interaction:
- return True, None
-
- # If we were trans-paused and rv is true, suppress
- # transitions up to the next interaction.
- if trans_pause and rv:
- self.suppress_transition = True
-
- # But wait, there's more! The finally block runs some cleanup
- # after this.
- return False, rv
-
- except EndInteraction as e:
- return False, e.value
-
- finally:
-
- renpy.exports.say_attributes = None
-
- # Clean out the overlay layers.
- for i in renpy.config.overlay_layers:
- scene_lists.clear(i)
-
- # Stop ongoing preloading.
- renpy.display.im.cache.end_tick()
-
- # We no longer disable periodic between interactions.
- # pygame.time.set_timer(PERIODIC, 0)
-
- pygame.time.set_timer(TIMEEVENT, 0)
- pygame.time.set_timer(REDRAW, 0)
-
- renpy.game.context().runtime += end_time - start_time
-
- # Restart the old interaction, which also causes a
- # redraw if needed.
- self.restart_interaction = True
-
- # print "It took", frames, "frames."
-
- def timeout(self, offset):
- if offset < 0:
- return
-
- if self.timeout_time:
- self.timeout_time = min(self.event_time + offset, self.timeout_time)
- else:
- self.timeout_time = self.event_time + offset
-