Compare commits

...

112 Commits

Author SHA1 Message Date
Tom Rothamel c12adecf9f Bump versions. 2013-06-26 08:05:53 -04:00
Tom Rothamel fc6013c5de Use the correct shader program for imageblends. 2013-06-26 07:57:29 -04:00
Tom Rothamel 3dd9f07fe2 Add changelog. 2013-06-26 07:53:04 -04:00
Tom Rothamel 5308fdd0b8 Skip sets proper values for config.skipping.
This:

* Displays the skip indicator.
* Allows for fast (to-next-menu) skipping.
* Fixes #97.
2013-06-24 22:37:49 -04:00
Tom Rothamel 273d1e296c Apply drag-drop fix. (Thank you, Kinsman.)
Fixes #99.
2013-06-24 22:24:10 -04:00
Tom Rothamel 152af7dc7b Changes to improve compatibility with very old Ren'Py games.
These were required to get the Ren'Py 5.0 demo to run. (You'll need
to create a script_version.rpy file set to (5, 0, 0) to get things
actually working.)
2013-06-20 17:58:15 -04:00
Tom Rothamel 43b34f22a3 Bump version. 2013-06-17 20:22:49 -04:00
Tom Rothamel 3330b451d0 Build on android-8. 2013-06-17 20:22:38 -04:00
Tom Rothamel 5290296952 Print the android savedir to stdout/logcat.
I just have the feeling this will come in handy one day.
2013-06-17 01:58:14 -04:00
Tom Rothamel 8fc784aef5 Place logs and traceback.txt into renpy.config.logdir.
On android, this is always placed on external storage, so we can
get to it.
2013-06-17 01:51:19 -04:00
Tom Rothamel 110c8b890b Change the search path for saves on android.
We look in:

* The public directory used by very old RAPT versions.
* The private directory used by newer RAPT versions.
* The getExternalFilesDir directory, used by the newest RAPT versions.

This means that saves will now be placed in a public place, so they
can be copied and otherwise shared.
2013-06-17 01:36:27 -04:00
Tom Rothamel b6c686d770 Do not looks for assets on the android filesystem.
In recent RAPTs we won't put anything there. What's worse, older games
might have left files there, which will interfere with our newer
code.
2013-06-17 00:07:54 -04:00
Tom Rothamel 6358db88ff Never invoke the editor on android. 2013-06-17 00:07:44 -04:00
Tom Rothamel b2611fd613 Use different shaders for clipping and no-clipping cases.
The clipping support in the shaders causes a noticable performance
hit on Android, and is rarely used. This fixes that in the common
case.
2013-06-16 22:25:46 -04:00
Tom Rothamel 5eb3af0188 Update android packaging docs to match the new RAPT. 2013-06-10 01:45:13 -04:00
Tom Rothamel ad71c17f77 Prevent import recursion that took place if utf-8 was not imported
before the Ren'Py loader was initialized.

This actually happend on Android.
2013-06-10 01:36:52 -04:00
Tom Rothamel 0d7dec8e4e Merge pull request #109 from JaHIY/master
add a variable set the minimum number of component in automatic_images
2013-06-07 20:26:29 -07:00
Tom Rothamel 112fbbfc08 Return the correct value from renpy.call_in_new_context. 2013-06-07 23:02:15 -04:00
JaHIY 4ee5240c72 change config.automatic_images_minimum_component to
config.automatic_images_minimum_components
2013-06-07 22:29:34 +08:00
JaHIY 025e359394 Now developers can set the minimum number of component 2013-06-07 20:04:31 +08:00
Tom Rothamel 143bcfad08 Enable minimize and quit shortcuts in the launcher. 2013-06-05 10:11:27 -04:00
Tom Rothamel a39ebd13e0 Check in the changelog. 2013-06-04 23:43:42 -04:00
Tom Rothamel e7c0a5f19f Check in problems.rst. 2013-06-03 23:26:46 -04:00
Tom Rothamel f0b03dc426 Add the Flatten displayable to Ren'Py. 2013-06-03 23:24:53 -04:00
Tom Rothamel 3d2ad79b6b Document additive blending. 2013-06-03 23:04:13 -04:00
Tom Rothamel 9f28a93c56 Deal with r.over in the sprite manager. 2013-06-03 23:00:25 -04:00
Tom Rothamel 386dd1d799 Deal (improperly) with additive blending in the software renderer.
When additive blending is 1.0, the software renderer will not
display an image. This is probably preferable to the image
displaying incorrectly, since they're likely to be effect
images.
2013-06-03 22:55:28 -04:00
Tom Rothamel e85ebf3a50 Add additive blending to Transform. 2013-06-03 22:30:05 -04:00
Tom Rothamel 81eb2e6f42 Additive blending in the GL renderer.
This commit adds in the backend support, for varying the additive
blending factor.

Additive blending will not be added to the (mostly obsolete) software
renderer. Instead, added a way to test if the current renderer
supports additive blending.
2013-06-03 22:11:29 -04:00
Tom Rothamel 9d021a2ec1 Bump version to 6.15.5. 2013-06-02 23:32:06 -04:00
Tom Rothamel 439fac65e2 Remove assert statements.
Thes won't run when optimization is enabled, which is the default
for Ren'Py. It's better to eliminate them entirely, so we have
the same configuration in testing and production.
2013-06-02 20:46:19 -04:00
Tom Rothamel dee484a16b Error message fix. 2013-06-02 16:57:16 -04:00
Tom Rothamel 90457d6f20 Fix translation problem.
Returned the wrong value when we used RENPY_UPDATE_STRINGS
to translate a string with a {#translation_comment} in it.
2013-06-02 13:42:16 -04:00
Tom Rothamel 245c30d7a7 Force font autohinting.
This was the behavior of of Ren'Py until switching to Freetype 2.4. The
autohinter more resembles the older Ren'Pys, seems to produce better
forms on most fonts, and fixes spacing issues on broken fonts.
2013-05-27 23:39:07 -04:00
Tom Rothamel 81b28293ca Allow * in classification patterns to match directories as well as
files.

Fixes #106.
2013-05-26 00:10:08 -04:00
Tom Rothamel 392f71b214 Use secure permissions in zip and tar files.
Previously, we used 666 and 777, and expected the user's umask to take
care of adjusting the permissions. Unfortunately, info-zip overrides
the umask, writing world-writable files to disk.

Thanks to Spiky Caterpillar for the bug and the fix.
2013-05-23 00:37:26 -04:00
Tom Rothamel 257a5281ed Make use of avresample optional.
This lets us compile on platforms that do not support it, using
the old audio_resample code.
2013-05-21 00:25:09 -04:00
Tom Rothamel d44f70a145 Revert "Update ffdecode.c to work with newer libav."
This reverts commit 1ffd4f97fc.
2013-05-21 00:00:45 -04:00
Tom Rothamel ec9198f25d Fix one more deprecation with newer libav. 2013-05-20 23:54:37 -04:00
Tom Rothamel 1ffd4f97fc Update ffdecode.c to work with newer libav.
This gets rid of deprecation warnings when building ffdecode.c, and
adds a dependency on the avsample library.
2013-05-20 23:28:14 -04:00
Tom Rothamel e8968252ab Skip whitespace before default parameters and arguments.
Fixes #101.
2013-05-06 22:58:34 -04:00
Tom Rothamel ae618ddd76 Allow the creator to cancel out of create file and generate translation.
Fixes #100.
2013-05-04 12:24:30 -04:00
Tom Rothamel 005aa1a64c Read assets from an expansion file if one is available on Android. 2013-04-14 22:51:55 -04:00
Tom Rothamel a63231d36f Merge branch 'master' into android 2013-04-13 23:06:26 -04:00
Tom Rothamel db530f7def Document config.start_callbacks. 2013-04-07 20:00:47 -04:00
Tom Rothamel 627261f51d Clarify what the Ren'Py license applies to. 2013-04-07 19:43:58 -04:00
Tom Rothamel d080b71f29 Make exclude_empty_directories deal with non-terminal directories. 2013-04-07 11:13:00 -04:00
Tom Rothamel 0a62195625 Update changelog, document problems. 2013-04-06 23:11:51 -04:00
Tom Rothamel 571bb43459 Bump version to 6.15.4. 2013-04-06 23:00:10 -04:00
Tom Rothamel 817b9d5c9f Fix race condition in the updater.
If the update finished while we were checking progress, zsync
wouldn't rename the file and the update would fail.
2013-04-06 22:55:30 -04:00
Tom Rothamel 6f3fa9bf8d Make loads after rollback non-greedy.
A greedy load after rollback can roll-back through with statements,
which are turned into with None statements until the start of the
next interaction. (Which is sensible behavior for the rollback
case, but fails when an after_load transition is scheduled.)

Fixes #95.
2013-04-06 21:13:47 -04:00
Tom Rothamel 2de5a7d9f7 Add build.exclude_empty_directories variable. 2013-04-06 20:58:10 -04:00
Tom Rothamel d26f1a65e8 Add the ability to include empty directories to the zip format.
Per http://lemmasoft.renai.us/forums/viewtopic.php?f=32&t=2023
2013-04-06 18:31:59 -04:00
Tom Rothamel 667c2e59d3 Do not set the window size while entering/leaving fullscreen mode. 2013-04-06 17:52:12 -04:00
Tom Rothamel b4411a027f Fix documentation issues.
Fixes #89.
2013-04-06 13:53:20 -04:00
Tom Rothamel 142831ec44 Remove duplicated docs.
Fixes #90.
2013-04-06 13:19:48 -04:00
Tom Rothamel 2dd58759de Remove extraneous debug print. 2013-04-04 19:06:55 -04:00
Tom Rothamel 866f3a11b2 Use nogil_copy to copy surfaces.
Previously, we used the angle copy function to copy surfaces, which
used the pygame blit function. The pygame blit function was more
complex and held the GIL while blitting, and led to slowdowns in
loading.

Nogil_copy appears to be safe for Ren'Py, which doesn't use software
surfaces.
2013-04-04 12:13:56 -04:00
Tom Rothamel 8a2d779320 Spend less time with the image cache locked.
The image cache is no longer locked for expensive operations,
such as image loads. While this may cause us to load an image
twice (in rare circumstances), it also lets us preload images
without blocking loads frome the main thread.
2013-04-04 03:22:36 -04:00
Tom Rothamel 2ef8cd4c3f Remove obsolete renpy_font module. 2013-04-02 23:42:21 -04:00
Tom Rothamel 88ec1e66e6 Use rwobject.c that's included in pygame, rather than building
our own.
2013-04-02 23:40:32 -04:00
Tom Rothamel f6bfeefc8e Merge branch 'master' into android 2013-04-02 20:05:55 -04:00
Tom Rothamel bbdf9023fe Check in tkaskdir. 2013-04-02 20:05:34 -04:00
Tom Rothamel b0070122f8 Merge branch 'master' into android 2013-03-31 21:42:49 -04:00
Tom Rothamel ad51bfb30f Kick off preload thread at the start of image prediction, rather
than the end of an interaction.

Kicking it off at the end of the interaction lead to bad behavior
where the start of the next interaction would fight with the
preload thread for the image cache lock.
2013-03-31 17:56:16 -04:00
Tom Rothamel d15ae51825 New add.py script that combines the old add.sh and tag.py scripts,
and also does the dl symlinking.
2013-03-30 12:51:03 -04:00
Tom Rothamel 2efa265983 Bump version. 2013-03-30 11:50:21 -04:00
Tom Rothamel cc6513c93a Paranoia regarding cases where Transform.event is called before
Transform.render.

Closes #87.
2013-03-29 23:40:41 -04:00
Tom Rothamel a81865484c Ensure that we render displayables before passing events to them,
even if video is playing.
2013-03-29 23:34:11 -04:00
Tom Rothamel d70eb13cb0 Wait until a frame is available before drawing the screen while
displaying full-screen video.

Fixes #85.
2013-03-29 23:18:00 -04:00
Tom Rothamel 4b5d94eeb9 Re-offset a viewport if its size changes before the first event has
been processed.

This is necessary to deal with the way a size may offer the viewport
a smaller area than initially necessary, once scrollbars take up
some space.

Fixes #88.
2013-03-29 20:32:51 -04:00
Tom Rothamel 5674a4ddac Produce an error when dealing with logical lines longer than 64K
characters in size.

Line parsing can be N**2, so this turns a really slow error into one
that takes a reasonable amount of time.
2013-03-29 00:28:13 -04:00
Tom Rothamel 5bb6483ab4 When a page is given specified to FileSave, use it to determine
if the action should be sensitive.

This was preventing QuickSave from working when the default file page
was set to auto.

Fixes #82.
2013-03-29 00:15:14 -04:00
Tom Rothamel f62e4b6eb7 Add more protection to pygame.event.post statements.
Fixes #86.
2013-03-28 23:46:30 -04:00
Tom Rothamel c54a613393 Bars only run hover and unhover actions once. 2013-03-28 22:57:12 -04:00
Tom Rothamel 2c4be00267 Ensure image fonts include U+200B, since we need that for
input to work.
2013-03-27 21:04:29 -04:00
Tom Rothamel 0b8c22e0d8 Sort SpriteManager sprites by their zorder.
Thanks to Kinsman for the fix.
2013-03-27 10:11:45 -04:00
Tom Rothamel 65a7dca95f Fix random segfaulting related to TTF lookup tables, especially on shift+R.
The segfaulting occured when a TTF Font contains a GSUB table with a
Lookup type other than one. This caused us to initialize a data
structure that represents the lookup table, but never fill it in.

Rarely, one of these tables would hava field initalized to 1 or 2. In
that case, we'd try to free associated data that was never properly
initialized, and we'd crash.

The fix was to intialize the data structures before use.
2013-03-27 00:06:48 -04:00
Tom Rothamel fdcfaface0 Do inexpensive prediction while displaying frames.
This involved breaking the prediction coroutine, and the context
prediction routine up further. By predicting one statement at a time,
we can interleve predicitions with frame display, and prevent
dissolves or ATL transforms from starving the image predictor, at
least in simple (non-screen) cases.
2013-03-26 00:58:33 -04:00
Tom Rothamel 5f08884b52 Predict images used in ATL transforms that are part of show/scene
statements.
2013-03-25 23:36:46 -04:00
Tom Rothamel 3595eee11c Prevent memory leaks via the rollback log.
Before this change, code that created and then immediately modified a
list, dict, set, or object that was rollback-able would cause a small
memory leak until the end of the interaction.
2013-03-24 23:51:13 -04:00
Tom Rothamel b33eb54c34 Bound the layout cache size.
This helps ensure that code that creates many text objects in a
single interaction will now consume unbounded memory if it lets
those Text objects be GCed.
2013-03-24 23:49:02 -04:00
Tom Rothamel c86fc743bc Change memory profile to show the differen between the number of
objects at each call.
2013-03-24 23:48:39 -04:00
Tom Rothamel 10b82603b9 Do not translate the who-prefix or who-suffix if new-style
subsititutions are disabled.
2013-03-23 20:13:04 -04:00
Tom Rothamel 1a1ae36299 Quit the display when doing a forced resize (like when clicking the
Window preference.)

This prevents the possibility of spurious resize events, and also
prevents the black bars from appearing on the screen after such
a resize.
2013-03-20 23:26:36 -04:00
Tom Rothamel cc004c00f0 Merge branch 'master' into android 2013-03-19 23:30:51 -04:00
Tom Rothamel cda96afd14 Ensure an over-full video queue can't starve the audio queue,
and vice-versa.
2013-03-19 00:04:18 -04:00
Tom Rothamel 966630ea16 Write .zip files out in large chunks.
Previously, we would load entire files into RAM, compress the entire
file, and write out the compressed data. This was breaking down on
large .rpas, hence the change.
2013-03-17 18:42:19 -04:00
Tom Rothamel 8c746bdccc Work around fonts that don't have the ZWSP (U+200b) character.
We use ZWSP in input so we properly size an empty line. Some fonts
do not include this character, and replace it with an undefined
character glyph (often a square).

This fix prevents Ren'Py from rendering the ZWSP, making the
problem invisible to the user.
2013-03-16 22:49:34 -04:00
Tom Rothamel 4fac7da3bb Start work on documenting the python statements. 2013-03-10 23:32:44 -04:00
Tom Rothamel 4045958cd2 Bump version. 2013-03-09 23:11:51 -05:00
Tom Rothamel 45155a9ff4 Use python+tkinter to prompt for the projects directory.
Fixes #77.
2013-03-09 23:02:21 -05:00
Tom Rothamel 83ad789654 Improve OpenGL ES2 support. 2013-03-08 23:44:10 -05:00
Tom Rothamel 21eaa5ee9c Merge branch 'master' into android 2013-03-08 21:31:50 -05:00
Tom Rothamel 69bcd84d5d Fix doc typos.
Thanks, Koichi.
2013-03-08 00:57:07 -05:00
Tom Rothamel 7357fa4c5b Allow the music room to loop tracks, rather than advancing them. 2013-03-07 11:38:32 -05:00
Tom Rothamel d51c768112 Document renpy.input. 2013-03-06 22:44:28 -05:00
Tom Rothamel 32ae4fef3a Bump version. 2013-03-04 10:09:20 -05:00
Tom Rothamel fd5ee77f1c Force use of shaders on android.
The auto-detect code fails on ES.
2013-03-04 01:15:56 -05:00
Tom Rothamel b9acbfccd7 Changes required to support android.
The new android support uses GL2, so we need to change some defines
to use it.
2013-03-04 00:00:08 -05:00
Tom Rothamel d05059e64d Stop ongoing transitions when we enter a new context.
A minor performance improvement, which also prevents the side-effects
of the new code being surrounded by old transitions.
2013-03-03 01:08:10 -05:00
Tom Rothamel bc80e38bb5 Change how we compensate for times in a restarted move transition.
The previous method was causing problems where when we created
a new screen under a transition, the new screen would get the
wrong creation time.
2013-03-03 00:48:29 -05:00
Tom Rothamel 838233a68f Doc fix. 2013-03-02 21:27:35 -05:00
Tom Rothamel 6968d973a6 Fix compatibility between OldMoveTransition and define.move_transitions.
Fixes #74.
2013-03-02 12:28:53 -05:00
Tom Rothamel 199f6aa70c Include the language as part of a rollback copy.
This prevents us from running the language change code whenever we
do a rollback when a non-default language is enabled.

Fixes #73.
2013-02-28 22:30:29 -05:00
Tom Rothamel b7e33bb2b9 Documentation fix. 2013-02-28 00:01:44 -05:00
Tom Rothamel d2d777be0c Doc fix. 2013-02-27 23:59:49 -05:00
Tom Rothamel 4c924b4971 Documentation fix. 2013-02-27 23:55:41 -05:00
Tom Rothamel ddd4a1a4ea Fix mistake. 2013-02-27 23:54:57 -05:00
Tom Rothamel c68f240061 Fix more launch options. 2013-02-25 09:42:49 -05:00
Tom Rothamel 5d9d4955ff Allow symlinks to renpy.py. 2013-02-25 09:26:35 -05:00
Tom Rothamel fb4eeaaaeb Update sphinx docs. 2013-02-24 22:51:49 -05:00
105 changed files with 1925 additions and 3930 deletions
Executable
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python
import argparse
import os
import subprocess
from renpy import version_tuple #@UnresolvedImport
version = ".".join(str(i) for i in version_tuple)
short_version = ".".join(str(i) for i in version_tuple[:3])
print "Version", version
ap = argparse.ArgumentParser()
ap.add_argument("--release", action="store_true")
ap.add_argument("--prerelease", action="store_true")
ap.add_argument("--experimental", action="store_true")
ap.add_argument("--no-tag", "-n", action="store_true")
args = ap.parse_args()
if args.release:
links = [ "release", "prerelease", "experimental" ]
tag = True
elif args.prerelease:
links = [ "prerelease", "experimental" ]
tag = True
elif args.experimental:
links = [ "experimental" ]
tag = False
else:
links = [ ]
tag = False
os.chdir("/home/tom/ab/renpy/dl")
for i in links:
if os.path.exists(i):
os.unlink(i)
os.symlink(short_version, i)
os.chdir("/home/tom/ab/renpy")
if tag and not args.no_tag:
cmd = [ "git", "tag", "-a", version, "-m", "Ren'Py " + version ]
subprocess.check_call(cmd)
os.chdir("/home/tom/ab/website")
subprocess.check_call("./upload.sh")
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
cd ~/ab/website
./upload.sh
+1 -1
View File
@@ -112,7 +112,7 @@ display: none;
<dd>Toggles fullscreen mode</dd>
<dt>S</dt>
<dd>Takes a screenshot, saving it in a file named screenshotxxxx.png, where xxxx is a serial number.</dd>
<dt>Alt-H, Command-H</dt>
<dt>Alt-M, Command-H</dt>
<dd>Hides (iconifies) the window.</dd>
<dt>Alt-F4, Command-Q</dt>
<dd>Quits the game.</dd>
+1 -1
View File
@@ -4,7 +4,7 @@ label add_file:
import os
import codecs
filename = interface.input(_("FILENAME"), _("Enter the name of the script file to create."), filename="withslash")
filename = interface.input(_("FILENAME"), _("Enter the name of the script file to create."), filename="withslash", cancel=Jump("navigation"))
if "." in filename and not filename.endswith(".rpy"):
interface.error(_("The filename must have the .rpy extension."), label="navigation")
+24 -1
View File
@@ -47,7 +47,7 @@ init python in distribute:
regexp += r'.*'
pattern = pattern[2:]
elif pattern[0] == "*":
regexp += r'[^/]*'
regexp += r'[^/]*/?'
pattern = pattern[1:]
elif pattern[0] == '[':
regexp += r'['
@@ -152,6 +152,26 @@ init python in distribute:
return rv
def filter_empty(self):
"""
Makes a deep copy of this file list with empty directories
omitted.
"""
rv = FileList()
needed_dirs = set()
for i in reversed(self):
if (not i.directory) or (i.name in needed_dirs):
rv.insert(0, i.copy())
directory, _sep, _filename = i.name.rpartition("/")
needed_dirs.add(directory)
return rv
@staticmethod
def merge(l):
"""
@@ -675,6 +695,9 @@ init python in distribute:
fl = fl.copy()
fl.sort()
if self.build.get("exclude_empty_directories", True):
fl = fl.filter_empty()
# Write the update information.
update_files = [ ]
update_xbit = [ ]
+8 -1
View File
@@ -174,7 +174,14 @@ init -1 python hide:
_game_menu_screen = None
config.underlay = [ ]
config.underlay = [
renpy.Keymap(
quit = renpy.quit_event,
iconify = renpy.iconify,
choose_renderer = renpy.curried_call_in_new_context("_choose_renderer"),
),
]
config.rollback_enabled = False
+91 -11
View File
@@ -7,16 +7,88 @@ init python in distribute:
import zipfile
import tarfile
import zlib
import struct
import stat
from zipfile import crc32
zlib.Z_DEFAULT_COMPRESSION = 9
class ZipFile(zipfile.ZipFile):
def write_with_info(self, zinfo, filename):
"""Put the bytes from filename into the archive under the name
arcname."""
if not self.fp:
raise RuntimeError(
"Attempt to write to ZIP archive that was already closed")
st = os.stat(filename)
isdir = stat.S_ISDIR(st.st_mode)
zinfo.file_size = st.st_size
zinfo.flag_bits = 0x00
zinfo.header_offset = self.fp.tell() # Start of header bytes
self._writecheck(zinfo)
self._didModify = True
if isdir:
zinfo.file_size = 0
zinfo.compress_size = 0
zinfo.CRC = 0
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
self.fp.write(zinfo.FileHeader())
return
with open(filename, "rb") as fp:
# Must overwrite CRC and sizes with correct data later
zinfo.CRC = CRC = 0
zinfo.compress_size = compress_size = 0
zinfo.file_size = file_size = 0
self.fp.write(zinfo.FileHeader())
if zinfo.compress_type == zipfile.ZIP_DEFLATED:
cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
zlib.DEFLATED, -15)
else:
cmpr = None
while 1:
buf = fp.read(1024 * 1024)
if not buf:
break
file_size = file_size + len(buf)
CRC = crc32(buf, CRC) & 0xffffffff
if cmpr:
buf = cmpr.compress(buf)
compress_size = compress_size + len(buf)
self.fp.write(buf)
if cmpr:
buf = cmpr.flush()
compress_size = compress_size + len(buf)
self.fp.write(buf)
zinfo.compress_size = compress_size
else:
zinfo.compress_size = file_size
zinfo.CRC = CRC
zinfo.file_size = file_size
# Seek backwards and write CRC and file sizes
position = self.fp.tell() # Preserve current position in file
self.fp.seek(zinfo.header_offset + 14, 0)
self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
zinfo.file_size))
self.fp.seek(position, 0)
self.filelist.append(zinfo)
self.NameToInfo[zinfo.filename] = zinfo
class ZipPackage(object):
"""
A class that creates a zip file.
"""
def __init__(self, filename):
self.zipfile = zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED)
self.zipfile = ZipFile(filename, "w", zipfile.ZIP_DEFLATED)
def add_file(self, name, path, xbit):
@@ -31,18 +103,26 @@ init python in distribute:
zi.create_system = 3
if xbit:
zi.external_attr = long(0100777) << 16
zi.external_attr = long(0100755) << 16
else:
zi.external_attr = long(0100666) << 16
zi.external_attr = long(0100644) << 16
with open(path, "rb") as f:
data = f.read()
self.zipfile.writestr(zi, data)
self.zipfile.write_with_info(zi, path)
def add_directory(self, name, path):
return
if path is None:
return
zi = zipfile.ZipInfo(name + "/")
s = os.stat(path)
zi.date_time = time.gmtime(s.st_mtime)[:6]
zi.compress_type = zipfile.ZIP_STORED
zi.create_system = 3
zi.external_attr = (long(0040755) << 16) | 0x10
self.zipfile.write_with_info(zi, path)
def close(self):
self.zipfile.close()
@@ -70,9 +150,9 @@ init python in distribute:
info.type = tarfile.DIRTYPE
if xbit:
info.mode = 0777
info.mode = 0755
else:
info.mode = 0666
info.mode = 0644
info.uid = 1000
info.gid = 1000
+13 -24
View File
@@ -5,13 +5,10 @@
init python:
try:
import EasyDialogs
except ImportError:
try:
import EasyDialogsWin as EasyDialogs
except:
EasyDialogs = None
import EasyDialogsWin as EasyDialogs
except:
EasyDialogs = None
import os
init python in project:
@@ -485,16 +482,13 @@ label choose_projects_directory:
python hide:
interface.interaction(_("PROJECTS DIRECTORY"), _("Please choose the projects directory."), _("This launcher will scan for projects in this directory, will create new projects in this directory, and will place built projects into this directory."))
interface.interaction(_("PROJECTS DIRECTORY"), _("Please choose the projects directory using the directory chooser.\n{b}The directory chooser may have opened behind this window.{/b}"), _("This launcher will scan for projects in this directory, will create new projects in this directory, and will place built projects into this directory."),)
path = persistent.projects_directory
if path:
default_path = path
else:
try:
default_path = os.path.dirname(os.path.abspath(config.renpy_base))
except:
@@ -513,27 +507,22 @@ label choose_projects_directory:
else:
try:
env = os.environ.copy()
if 'RENPY_ORIGINAL_LD_LIBRARY_PATH' in env:
env['LD_LIBRARY_PATH'] = env['RENPY_ORIGINAL_LD_LIBRARY_PATH']
cmd = [ "zenity", "--title=Select Projects Directory", "--file-selection", "--directory", "--filename=" + renpy.fsencode(default_path) ]
zen = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE)
choice = zen.stdout.read()
zen.wait()
cmd = [ "/usr/bin/python", os.path.join(config.gamedir, "tkaskdir.py"), renpy.fsencode(default_path) ]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
choice = p.stdout.read()
p.wait()
if choice:
path = renpy.fsdecode(choice[:-1])
path = renpy.fsdecode(choice)
except:
import traceback
traceback.print_exc()
path = None
interface.error(_("Ren'Py was unable to run zenity to choose the projects directory."), label=None)
interface.error(_("Ren'Py was unable to run python with tkinter to choose the projects directory."), label=None)
if path is None:
path = default_path
+1 -1
View File
@@ -1,4 +1,4 @@
init -999:
$ config.script_version = (6, 15, 0)
$ config.script_version = (6, 15, 7)
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
# This is used on Linux and Mac to prompt the user for the projects
# directory.
import sys
# Python3 and Python2-style imports.
try:
from tkinter import Tk
from tkinter.filedialog import askdirectory
except ImportError:
from Tkinter import Tk
from tkFileDialog import askdirectory
# Create the TK canvas.
if __name__ == "__main__":
root = Tk()
root.withdraw()
result = askdirectory(initialdir=sys.argv[1], parent=root, title="Select Ren'Py Projects Directory")
sys.stdout.write(result)
+1 -1
View File
@@ -7,7 +7,7 @@ label translate:
python:
language = interface.input(_("Create or Update Translations"), _("Please enter the name of the language for which you want to create or update translations."), filename=True, default=persistent.translate_language)
language = interface.input(_("Create or Update Translations"), _("Please enter the name of the language for which you want to create or update translations."), filename=True, default=persistent.translate_language, cancel=Jump("front_page"))
language = language.strip()
+8 -3
View File
@@ -23,13 +23,16 @@
def version():
return (6, 12, 0)
cdef extern from "renpy.h":
cdef extern from "pygame/pygame.h":
cdef struct SDL_RWops:
pass
void import_pygame_rwobject()
SDL_RWops* RWopsFromPython(object obj)
cdef extern from "renpy.h":
void core_init()
void save_png_core(object, SDL_RWops *, int)
@@ -421,5 +424,7 @@ def subpixel(pysrc, pydst, xoffset, yoffset, shift):
# Be sure to update scale.py when adding something new here!
import_pygame_rwobject()
core_init()
+237 -28
View File
@@ -25,12 +25,12 @@
#include <libavutil/avstring.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
// #include "libavdevice/avdevice.h"
#include <libswscale/swscale.h>
// #include "libavcodec/audioconvert.h"
// #include "libavcodec/opt.h"
// #include "cmdutils.h"
#ifdef HAS_RESAMPLE
#include <libavutil/opt.h>
#include <libavresample/avresample.h>
#endif
#include <SDL.h>
#include <SDL_thread.h>
@@ -41,8 +41,8 @@
#undef exit
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
#define MIN_AUDIOQ_SIZE (20 * 16 * 1024)
#define MIN_FRAMES 5
/* SDL audio buffer size, in samples. Should be small to have precise
A/V sync as SDL does not have hardware buffer fullness info. */
@@ -100,17 +100,24 @@ typedef struct VideoState {
/* samples output by the codec. we reserve more space for avsync
compensation */
#ifndef HAS_RESAMPLE
uint8_t audio_buf1[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2] __attribute__ ((aligned (16))) ;
uint8_t audio_buf2[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2] __attribute__ ((aligned (16))) ;
#else
uint8_t *audio_buf1;
#endif
uint8_t *audio_buf;
unsigned int audio_buf_size; /* in bytes */
int audio_buf_index; /* in bytes */
AVPacket audio_pkt;
AVPacket audio_pkt_temp;
// AVAudioConvert *reformat_ctx;
#ifndef HAS_RESAMPLE
ReSampleContext *reformat_ctx;
#endif
int resample_frac;
int show_audio; /* if true, display audio samples */
@@ -165,6 +172,19 @@ typedef struct VideoState {
// Should we force the display of the current video frame?
int first_frame;
#ifdef HAS_RESAMPLE
// The audio frame, and the audio resample context.
enum AVSampleFormat sdl_sample_fmt;
uint64_t sdl_channel_layout;
int sdl_channels;
int sdl_sample_rate;
enum AVSampleFormat resample_sample_fmt;
uint64_t resample_channel_layout;
int resample_sample_rate;
AVAudioResampleContext *avr;
AVFrame *frame;
#endif
} VideoState;
SDL_mutex *codec_mutex = NULL;
@@ -183,7 +203,6 @@ static int debug_mv = 0;
static int workaround_bugs = 1;
static int fast = 0;
static int genpts = 0;
static int lowres = 0;
static int idct = FF_IDCT_AUTO;
static enum AVDiscard skip_frame= AVDISCARD_DEFAULT;
static enum AVDiscard skip_idct= AVDISCARD_DEFAULT;
@@ -635,7 +654,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
SDL_LockMutex(is->pictq_mutex);
while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
!is->videoq.abort_request) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
SDL_UnlockMutex(is->pictq_mutex);
@@ -660,7 +679,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts)
/* wait until the picture is allocated */
while (!vp->allocated && !is->videoq.abort_request) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
SDL_UnlockMutex(is->pictq_mutex);
@@ -770,8 +789,181 @@ static int video_thread(void *arg)
return 0;
}
#ifdef HAS_RESAMPLE
/* decode one audio frame and returns its uncompressed size */
static int audio_decode_frame(VideoState *is, double *pts_ptr)
{
AVPacket *pkt_temp = &is->audio_pkt_temp;
AVPacket *pkt = &is->audio_pkt;
AVCodecContext *dec = is->audio_st->codec;
int n, len1, data_size, got_frame;
double pts;
int new_packet = 0;
int flush_complete = 0;
for (;;) {
/* NOTE: the audio packet can contain several frames */
while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet)) {
int resample_changed, audio_resample;
if (!is->frame) {
if (!(is->frame = avcodec_alloc_frame()))
return AVERROR(ENOMEM);
} else
avcodec_get_frame_defaults(is->frame);
if (flush_complete)
break;
new_packet = 0;
len1 = avcodec_decode_audio4(dec, is->frame, &got_frame, pkt_temp);
if (len1 < 0) {
/* if error, we skip the frame */
pkt_temp->size = 0;
break;
}
pkt_temp->data += len1;
pkt_temp->size -= len1;
if (!got_frame) {
/* stop sending empty packets if the decoder is finished */
if (!pkt_temp->data && dec->codec->capabilities & CODEC_CAP_DELAY)
flush_complete = 1;
continue;
}
data_size = av_samples_get_buffer_size(NULL, dec->channels,
is->frame->nb_samples,
is->frame->format, 1);
audio_resample = is->frame->format != is->sdl_sample_fmt ||
is->frame->channel_layout != is->sdl_channel_layout ||
is->frame->sample_rate != is->sdl_sample_rate;
resample_changed = is->frame->format != is->resample_sample_fmt ||
is->frame->channel_layout != is->resample_channel_layout ||
is->frame->sample_rate != is->resample_sample_rate;
if ((!is->avr && audio_resample) || resample_changed) {
int ret;
if (is->avr)
avresample_close(is->avr);
else if (audio_resample) {
is->avr = avresample_alloc_context();
if (!is->avr) {
fprintf(stderr, "error allocating AVAudioResampleContext\n");
break;
}
}
if (audio_resample) {
av_opt_set_int(is->avr, "in_channel_layout", is->frame->channel_layout, 0);
av_opt_set_int(is->avr, "in_sample_fmt", is->frame->format, 0);
av_opt_set_int(is->avr, "in_sample_rate", is->frame->sample_rate, 0);
av_opt_set_int(is->avr, "out_channel_layout", is->sdl_channel_layout, 0);
av_opt_set_int(is->avr, "out_sample_fmt", is->sdl_sample_fmt, 0);
av_opt_set_int(is->avr, "out_sample_rate", is->sdl_sample_rate, 0);
if ((ret = avresample_open(is->avr)) < 0) {
fprintf(stderr, "error initializing libavresample\n");
break;
}
}
is->resample_sample_fmt = is->frame->format;
is->resample_channel_layout = is->frame->channel_layout;
is->resample_sample_rate = is->frame->sample_rate;
}
if (audio_resample) {
void *tmp_out;
int out_samples, out_size, out_linesize;
int osize = av_get_bytes_per_sample(is->sdl_sample_fmt);
int nb_samples = is->frame->nb_samples;
int max_samples = 2 * (avresample_get_delay(is->avr) + nb_samples) * is->sdl_sample_rate / is->frame->sample_rate;
out_size = av_samples_get_buffer_size(
&out_linesize,
is->sdl_channels,
max_samples,
is->sdl_sample_fmt, 0);
tmp_out = av_realloc(is->audio_buf1, out_size);
if (!tmp_out)
return AVERROR(ENOMEM);
is->audio_buf1 = tmp_out;
out_samples = avresample_convert(is->avr,
&is->audio_buf1,
out_linesize, max_samples,
is->frame->data,
is->frame->linesize[0],
is->frame->nb_samples);
if (out_samples < 0) {
fprintf(stderr, "avresample_convert() failed\n");
break;
}
is->audio_buf = is->audio_buf1;
data_size = out_samples * osize * is->sdl_channels;
} else {
is->audio_buf = is->frame->data[0];
}
/* if no pts, then compute it */
pts = is->audio_clock;
*pts_ptr = pts;
n = is->sdl_channels * av_get_bytes_per_sample(is->sdl_sample_fmt);
is->audio_clock += (double)data_size /
(double)(n * is->sdl_sample_rate);
// This is Ren'Py specific code, to deal with ogg files with
// more data than their duration.
if (is->audio_duration) {
int len = data_size / 4;
int maxlen = is->audio_duration - is->audio_played;
if (len > maxlen) {
len = maxlen;
}
is->audio_played += len;
data_size = len * 4;
}
return data_size;
}
/* free the current packet */
if (pkt->data)
av_free_packet(pkt);
memset(pkt_temp, 0, sizeof(*pkt_temp));
if (is->paused || is->audioq.abort_request) {
return -1;
}
/* read next packet */
if ((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0)
return -1;
if (pkt->data == flush_pkt.data) {
avcodec_flush_buffers(dec);
flush_complete = 0;
}
*pkt_temp = *pkt;
/* if update the audio clock with the pts */
if (pkt->pts != AV_NOPTS_VALUE) {
is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
}
}
}
#else
/* decode one audio frame and returns its uncompressed size */
static int audio_decode_frame(VideoState *is, double *pts_ptr)
@@ -823,17 +1015,17 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
}
// Moved by tom, from below next block. Is this right?
if (is->reformat_ctx) {
int len = data_size / av_get_bytes_per_sample(dec->sample_fmt);
len /= dec->channels;
len = audio_resample(is->reformat_ctx, (short *) is->audio_buf2, (short *) is->audio_buf1, len);
data_size = len * 4;
is->audio_buf = is->audio_buf2;
is->audio_buf = is->audio_buf2;
} else {
is->audio_buf = is->audio_buf1;
}
@@ -846,7 +1038,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
int inpos;
int len = data_size / 4;
if (is->audio_buf == is->audio_buf1) {
in = (short *) is->audio_buf1;
out = (short *) is->audio_buf2;
@@ -860,9 +1052,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
// which seems reasonable.
in[2 * len] = 2 * in[2 * len - 2] - in[2 * len - 4];
in[2 * len + 1] = 2 * in[2 * len - 1] - in[2 * len - 3];
// The number of bytes of input consumed per output
// sample. Scaled by 1 << 14.
// sample. Scaled by 1 << 14.
in_per_out = (dec->sample_rate << 14) / audio_sample_rate;
len *= (1 << 14);
@@ -892,14 +1084,14 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
out[2 * outpos + 1] = a + (((b - a) * frac) >> 14);
outpos++;
inpos += in_per_out;
inpos += in_per_out;
}
// Store the fraction.
is->resample_frac = inpos & ((1 << 14) - 1);
data_size = outpos * 4;
is->audio_buf = (uint8_t *) out;
is->audio_buf = (uint8_t *) out;
}
@@ -915,7 +1107,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
if (is->audio_duration) {
int len = data_size / 4;
int maxlen = is->audio_duration - is->audio_played;
if (len > maxlen) {
len = maxlen;
}
@@ -923,7 +1115,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
is->audio_played += len;
data_size = len * 4;
}
return data_size;
}
@@ -953,6 +1145,9 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
}
}
#endif
/* get the current audio output buffer size, in samples. With SDL, we
cannot have a precise information */
static int audio_write_get_buf_size(VideoState *is)
@@ -1038,8 +1233,6 @@ static int stream_component_open(VideoState *is, int stream_index)
enc->debug_mv = debug_mv;
enc->debug = debug;
enc->workaround_bugs = workaround_bugs;
enc->lowres = lowres;
if(lowres) enc->flags |= CODEC_FLAG_EMU_EDGE;
enc->idct_algo= idct;
if(fast) enc->flags2 |= CODEC_FLAG2_FAST;
enc->skip_frame= skip_frame;
@@ -1069,6 +1262,20 @@ static int stream_component_open(VideoState *is, int stream_index)
is->audio_buf_size = 0;
is->audio_buf_index = 0;
#ifdef HAS_RESAMPLE
if (!enc->channel_layout)
enc->channel_layout = av_get_default_channel_layout(enc->channels);
if (!enc->channel_layout) {
fprintf(stderr, "%s: unable to guess channel layout\n", is->filename);
return -1;
}
is->sdl_sample_rate = audio_sample_rate;
is->sdl_channel_layout = AV_CH_LAYOUT_STEREO;
is->sdl_channels = av_get_channel_layout_nb_channels(is->sdl_channel_layout);
is->sdl_sample_fmt = AV_SAMPLE_FMT_S16;
#endif
memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
packet_queue_init(&is->audioq);
break;
@@ -1102,8 +1309,10 @@ static void stream_component_close(VideoState *is, int stream_index)
case AVMEDIA_TYPE_AUDIO:
packet_queue_abort(&is->audioq);
packet_queue_end(&is->audioq);
#ifndef HAS_RESAMPLE
if (is->reformat_ctx)
audio_resample_close(is->reformat_ctx);
#endif
break;
case AVMEDIA_TYPE_VIDEO:
packet_queue_abort(&is->videoq);
@@ -1290,12 +1499,12 @@ static int decode_thread(void *arg)
if (is->abort_request) {
break;
}
/* if the queue are full, no need to read more */
if (is->audioq.size > MAX_AUDIOQ_SIZE ||
is->videoq.size > MAX_VIDEOQ_SIZE) {
/* wait 2 ms - or wait for quit notify.*/
if ((is->audioq.size > MIN_AUDIOQ_SIZE || is->audio_stream < 0) &&
(is->videoq.nb_packets > MIN_FRAMES || is->video_stream < 0)) {
/* wait 2 ms - or wait for quit notify.*/
SDL_LockMutex(is->quit_mutex);
SDL_CondWaitTimeout(is->quit_cond, is->quit_mutex, 2);
SDL_UnlockMutex(is->quit_mutex);
@@ -1353,7 +1562,7 @@ fail:
if (is->video_stream >= 0)
stream_component_close(is, is->video_stream);
if (is->ic) {
av_close_input_file(is->ic);
avformat_close_input(&(is->ic));
is->ic = NULL;
}
+4 -1
View File
@@ -12,7 +12,7 @@
#if defined ANDROID
#define RENPY_GLES_1
#define RENPY_GLES_2
#elif defined ANGLE
@@ -54,7 +54,10 @@
#if defined RENPY_GLES_2
#ifndef ANDROID
#include <EGL/egl.h>
#endif
#include <GLES2/gl2.h>
typedef GLuint GLhandleARB;
+19 -16
View File
@@ -373,25 +373,28 @@ cdef extern from "pyfreetype.h":
FT_Error FT_Set_Char_Size(FT_Face, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt hres, FT_UInt vres)
FT_Error FT_Set_Pixel_Sizes(FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height)
ctypedef enum FT_Load_Flags:
FT_LOAD_DEFAULT
FT_LOAD_NO_SCALE
FT_LOAD_NO_HINTING
FT_LOAD_RENDER
FT_LOAD_NO_BITMAP
FT_LOAD_VERTICAL_LAYOUT
FT_LOAD_FORCE_AUTOHINT
FT_LOAD_CROP_BITMAP
FT_LOAD_PEDANTIC
FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
FT_LOAD_NO_RECURSE
FT_LOAD_IGNORE_TRANSFORM
FT_LOAD_MONOCHROME
FT_LOAD_LINEAR_DESIGN
FT_LOAD_SBITS_ONLY
FT_LOAD_NO_AUTOHINT
FT_Error FT_Load_Glyph(FT_Face face, FT_UInt glyph_index, FT_Int32 flags)
FT_Error FT_Load_Char(FT_Face face, FT_ULong char_code, FT_Int32 flags)
DEF FT_LOAD_DEFAULT = 0
DEF FT_LOAD_NO_SCALE = 1 << 0
DEF FT_LOAD_NO_HINTING = 1 << 1
DEF FT_LOAD_RENDER = 1 << 2
DEF FT_LOAD_NO_BITMAP = 1 << 3
DEF FT_LOAD_VERTICAL_LAYOUT = 1 << 4
DEF FT_LOAD_FORCE_AUTOHINT = 1 << 5
DEF FT_LOAD_CROP_BITMAP = 1 << 6
DEF FT_LOAD_PEDANTIC = 1 << 7
DEF FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 1 << 9
DEF FT_LOAD_NO_RECURSE = 1 << 10
DEF FT_LOAD_IGNORE_TRANSFORM = 1 << 11
DEF FT_LOAD_MONOCHROME = 1 << 12
DEF FT_LOAD_LINEAR_DESIGN = 1 << 13
DEF FT_LOAD_SBITS_ONLY = 1 << 14
DEF FT_LOAD_NO_AUTOHINT = 1 << 15
void FT_Set_Transform(FT_Face face, FT_Matrix *matrix, FT_Vector *delta)
+15
View File
@@ -3,6 +3,21 @@ cdef extern from "pygame/pygame.h":
int w
int h
int pitch
int flags
void *pixels
struct SDL_Rect:
int x
int y
int w
int h
SDL_Surface *PySurface_AsSurface(object)
int SDL_SetAlpha(SDL_Surface *surface, unsigned int flag, char alpha)
enum:
SDL_SRCALPHA
cdef extern int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) nogil
-2
View File
@@ -27,8 +27,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <Python.h>
#include <SDL/SDL.h>
SDL_RWops* RWopsFromPythonThreaded(PyObject* obj);
void PSS_play(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight, int paused);
void PSS_queue(int channel, SDL_RWops *rw, const char *ext, PyObject *name, int fadeout, int tight);
void PSS_stop(int channel);
+7 -2
View File
@@ -20,12 +20,15 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
cdef extern from "pss.h":
cdef extern from "pygame/pygame.h":
cdef struct SDL_RWops:
pass
void import_pygame_rwobject()
SDL_RWops* RWopsFromPythonThreaded(object obj)
cdef extern from "pss.h":
void PSS_play(int channel, SDL_RWops *rw, char *ext, object name, int fadein, int tight, int paused)
void PSS_queue(int channel, SDL_RWops *rw, char *ext, object name, int fadein, int tight)
void PSS_stop(int channel)
@@ -181,3 +184,5 @@ def movie_size():
def check_version(version):
if version < 2 or version > 4:
raise Exception("pysdlsound version mismatch.")
import_pygame_rwobject()
-2
View File
@@ -4,8 +4,6 @@
#include <Python.h>
#include <SDL/SDL.h>
SDL_RWops* RWopsFromPython(PyObject* obj);
void core_init(void);
void save_png_core(PyObject *pysurf, SDL_RWops *file, int compress);
-859
View File
@@ -1,859 +0,0 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
/*
* font module for pygame
*/
#include <Python.h>
#include <stdio.h>
#include <string.h>
#include <pygame/pygame.h>
#include "structmember.h"
#include "renpy_ttf.h"
typedef struct {
PyObject_HEAD
RENPY_TTF_Font* font;
PyObject* weakreflist;
} PyFontObject;
#define PyFont_AsFont(x) (((PyFontObject*)x)->font)
static int font_initialized = 0;
static char* font_defaultname = "freesansbold.ttf";
static PyObject* self_module = NULL;
static char* pkgdatamodule_name = "pygame.pkgdata";
static char* resourcefunc_name = "getResource";
static PyObject *font_resource(char *filename) {
PyObject* load_basicfunc = NULL;
PyObject* pkgdatamodule = NULL;
PyObject* resourcefunc = NULL;
PyObject* result = NULL;
pkgdatamodule = PyImport_ImportModule(pkgdatamodule_name);
if (!pkgdatamodule) goto font_resource_end;
resourcefunc = PyObject_GetAttrString(pkgdatamodule, resourcefunc_name);
if (!resourcefunc) goto font_resource_end;
result = PyObject_CallFunction(resourcefunc, "s", filename);
if (!result) goto font_resource_end;
if (PyFile_Check(result)) {
PyObject *tmp = PyFile_Name(result);
Py_INCREF(tmp);
Py_DECREF(result);
result = tmp;
}
font_resource_end:
Py_XDECREF(pkgdatamodule);
Py_XDECREF(resourcefunc);
Py_XDECREF(load_basicfunc);
return result;
}
static void font_autoquit(void)
{
if(font_initialized)
{
font_initialized = 0;
RENPY_TTF_Quit();
}
}
static PyObject* font_autoinit(PyObject* self, PyObject* arg)
{
if(!PyArg_ParseTuple(arg, ""))
return NULL;
if(!font_initialized)
{
PyGame_RegisterQuit(font_autoquit);
if(RENPY_TTF_Init())
return PyInt_FromLong(0);
font_initialized = 1;
}
return PyInt_FromLong(font_initialized);
}
/*DOC*/ static char doc_quit[] =
/*DOC*/ "pygame.font.quit() -> none\n"
/*DOC*/ "uninitialize the font module\n"
/*DOC*/ "\n"
/*DOC*/ "Manually uninitialize SDL_ttf's font system. It is safe to call\n"
/*DOC*/ "this if font is currently not initialized.\n"
/*DOC*/ ;
static PyObject* fontmodule_quit(PyObject* self, PyObject* arg)
{
if(!PyArg_ParseTuple(arg, ""))
return NULL;
font_autoquit();
Py_RETURN_NONE;
}
/*DOC*/ static char doc_init[] =
/*DOC*/ "pygame.font.init() -> None\n"
/*DOC*/ "initialize the display module\n"
/*DOC*/ "\n"
/*DOC*/ "Manually initialize the font module. Will raise an exception if\n"
/*DOC*/ "it cannot be initialized. It is safe to call this function if\n"
/*DOC*/ "font is currently initialized.\n"
/*DOC*/ ;
static PyObject* fontmodule_init(PyObject* self, PyObject* arg)
{
PyObject* result;
int istrue;
if(!PyArg_ParseTuple(arg, ""))
return NULL;
result = font_autoinit(self, arg);
istrue = PyObject_IsTrue(result);
Py_DECREF(result);
if(!istrue)
return RAISE(PyExc_SDLError, SDL_GetError());
Py_RETURN_NONE;
}
/*DOC*/ static char doc_get_init[] =
/*DOC*/ "pygame.font.get_init() -> bool\n"
/*DOC*/ "get status of font module initialization\n"
/*DOC*/ "\n"
/*DOC*/ "Returns true if the font module is currently intialized.\n"
/*DOC*/ ;
static PyObject* get_init(PyObject* self, PyObject* arg)
{
if(!PyArg_ParseTuple(arg, ""))
return NULL;
return PyInt_FromLong(font_initialized);
}
/* font object methods */
/*DOC*/ static char doc_font_get_height[] =
/*DOC*/ "Font.get_height() -> int\n"
/*DOC*/ "average height of font glyph\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the average size of each glyph in the font.\n"
/*DOC*/ ;
static PyObject* font_get_height(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontHeight(font));
}
/*DOC*/ static char doc_font_get_descent[] =
/*DOC*/ "Font.get_descent() -> int\n"
/*DOC*/ "gets the font descent\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the descent for the font. The descent is the number of\n"
/*DOC*/ "pixels from the font baseline to the bottom of the font.\n"
/*DOC*/ "With most fonts this is a negative number.\n"
/*DOC*/ ;
static PyObject* font_get_descent(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontDescent(font));
}
/*DOC*/ static char doc_font_get_ascent[] =
/*DOC*/ "Font.get_ascent() -> int\n"
/*DOC*/ "gets the font ascent\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the ascent for the font. The ascent is the number of\n"
/*DOC*/ "pixels from the font baseline to the top of the font.\n"
/*DOC*/ ;
static PyObject* font_get_ascent(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontAscent(font));
}
/*DOC*/ static char doc_font_get_linesize[] =
/*DOC*/ "Font.get_linesize() -> int\n"
/*DOC*/ "gets the font recommended linesize\n"
/*DOC*/ "\n"
/*DOC*/ "Returns the linesize for the font. Each font comes with it's own\n"
/*DOC*/ "recommendation for the spacing number of pixels between each line\n"
/*DOC*/ "of the font.\n"
/*DOC*/ ;
static PyObject* font_get_linesize(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(RENPY_TTF_FontLineSkip(font));
}
/*DOC*/ static char doc_font_get_bold[] =
/*DOC*/ "Font.get_bold() -> bool\n"
/*DOC*/ "status of the bold attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Get the current status of the font's bold attribute\n"
/*DOC*/ ;
static PyObject* font_get_bold(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_BOLD) != 0);
}
/*DOC*/ static char doc_font_set_bold[] =
/*DOC*/ "Font.set_bold(bool) -> None\n"
/*DOC*/ "assign the bold attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Enables or disables the bold attribute for the font. Making the\n"
/*DOC*/ "font bold does not work as well as you expect.\n"
/*DOC*/ ;
static PyObject* font_set_bold(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int style, val;
if(!PyArg_ParseTuple(args, "i", &val))
return NULL;
style = RENPY_TTF_GetFontStyle(font);
if(val)
style |= RENPY_TTF_STYLE_BOLD;
else
style &= ~RENPY_TTF_STYLE_BOLD;
RENPY_TTF_SetFontStyle(font, style);
Py_RETURN_NONE;
}
/*DOC*/ static char doc_font_get_italic[] =
/*DOC*/ "Font.get_italic() -> bool\n"
/*DOC*/ "status of the italic attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Get the current status of the font's italic attribute\n"
/*DOC*/ ;
static PyObject* font_get_italic(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_ITALIC) != 0);
}
/*DOC*/ static char doc_font_set_italic[] =
/*DOC*/ "Font.set_italic(bool) -> None\n"
/*DOC*/ "assign the italic attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Enables or disables the italic attribute for the font.\n"
/*DOC*/ ;
static PyObject* font_set_italic(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int style, val;
if(!PyArg_ParseTuple(args, "i", &val))
return NULL;
style = RENPY_TTF_GetFontStyle(font);
if(val)
style |= RENPY_TTF_STYLE_ITALIC;
else
style &= ~RENPY_TTF_STYLE_ITALIC;
RENPY_TTF_SetFontStyle(font, style);
Py_RETURN_NONE;
}
/*DOC*/ static char doc_font_get_underline[] =
/*DOC*/ "Font.get_underline() -> bool\n"
/*DOC*/ "status of the underline attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Get the current status of the font's underline attribute\n"
/*DOC*/ ;
static PyObject* font_get_underline(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong((RENPY_TTF_GetFontStyle(font)&RENPY_TTF_STYLE_UNDERLINE) != 0);
}
/*DOC*/ static char doc_font_set_underline[] =
/*DOC*/ "Font.set_underline(bool) -> None\n"
/*DOC*/ "assign the underline attribute\n"
/*DOC*/ "\n"
/*DOC*/ "Enables or disables the underline attribute for the font.\n"
/*DOC*/ ;
static PyObject* font_set_underline(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int style, val;
if(!PyArg_ParseTuple(args, "i", &val))
return NULL;
style = RENPY_TTF_GetFontStyle(font);
if(val)
style |= RENPY_TTF_STYLE_UNDERLINE;
else
style &= ~RENPY_TTF_STYLE_UNDERLINE;
RENPY_TTF_SetFontStyle(font, style);
Py_RETURN_NONE;
}
/*DOC*/ static char doc_font_render[] =
/*DOC*/ "Font.render(text, antialias, fore_RGBA, [back_RGBA]) -> Surface\n"
/*DOC*/ "render text to a new image\n"
/*DOC*/ "\n"
/*DOC*/ "Render the given text onto a new image surface. The given text\n"
/*DOC*/ "can be standard python text or unicode. Antialiasing will smooth\n"
/*DOC*/ "the edges of the font for a much cleaner look. The foreground\n"
/*DOC*/ "and background color are both RGBA, the alpha component is ignored\n"
/*DOC*/ "if given. If the background color is omitted, the text will have a\n"
/*DOC*/ "transparent background.\n"
/*DOC*/ "\n"
/*DOC*/ "Note that font rendering is not thread safe, therefore only one\n"
/*DOC*/ "thread can render text at any given time.\n"
/*DOC*/ "\n"
/*DOC*/ "Also, rendering smooth text with underlines will crash with SDL_ttf\n"
/*DOC*/ "less that version 2.0, be careful.\n"
/*DOC*/ "\n"
/*DOC*/ "If you pass an empty string, render() will return a blank surface\n"
/*DOC*/ "1 pixel wide and the same height as the font.\n"
/*DOC*/ ;
static PyObject* font_render(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int aa;
PyObject* text, *final;
PyObject* fg_rgba_obj, *bg_rgba_obj = NULL;
Uint8 rgba[4];
SDL_Surface* surf;
SDL_Color foreg, backg;
if(!PyArg_ParseTuple(args, "OiO|O", &text, &aa, &fg_rgba_obj, &bg_rgba_obj))
return NULL;
if(!RGBAFromObj(fg_rgba_obj, rgba))
return RAISE(PyExc_TypeError, "Invalid foreground RGBA argument");
foreg.r = rgba[0]; foreg.g = rgba[1]; foreg.b = rgba[2];
if(bg_rgba_obj)
{
if(!RGBAFromObj(bg_rgba_obj, rgba))
return RAISE(PyExc_TypeError, "Invalid background RGBA argument");
backg.r = rgba[0];
backg.g = rgba[1];
backg.b = rgba[2];
backg.unused = 0;
} else {
backg.r = 0;
backg.g = 0;
backg.b = 0;
backg.unused = 0;
}
if(!PyObject_IsTrue(text))
{
int height = RENPY_TTF_FontHeight(font);
surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, height, 32, 0xff<<16, 0xff<<8, 0xff, 0);
if(bg_rgba_obj)
{
Uint32 c = SDL_MapRGB(surf->format, backg.r, backg.g, backg.b);
SDL_FillRect(surf, NULL, c);
}
else
SDL_SetColorKey(surf, SDL_SRCCOLORKEY, 0);
}
else if(PyUnicode_Check(text))
{
PyObject* strob = PyEval_CallMethod(text, "encode", "(s)", "utf-8");
char *string = PyString_AsString(strob);
if(aa)
{
if(!bg_rgba_obj)
surf = RENPY_TTF_RenderUTF8_Blended(font, string, foreg);
else
surf = RENPY_TTF_RenderUTF8_Shaded(font, string, foreg, backg);
}
else
surf = RENPY_TTF_RenderUTF8_Solid(font, string, foreg);
Py_DECREF(strob);
}
else if(PyString_Check(text))
{
char* string = PyString_AsString(text);
if(aa)
{
if(!bg_rgba_obj)
surf = RENPY_TTF_RenderText_Blended(font, string, foreg);
else
surf = RENPY_TTF_RenderText_Shaded(font, string, foreg, backg);
}
else
surf = RENPY_TTF_RenderText_Solid(font, string, foreg);
}
else
return RAISE(PyExc_TypeError, "text must be a string or unicode");
if(!surf)
return RAISE(PyExc_SDLError, "SDL_ttf render failed");
if(!aa && bg_rgba_obj) /*turn off transparancy*/
{
SDL_SetColorKey(surf, 0, 0);
surf->format->palette->colors[0].r = backg.r;
surf->format->palette->colors[0].g = backg.g;
surf->format->palette->colors[0].b = backg.b;
}
final = PySurface_New(surf);
if(!final)
SDL_FreeSurface(surf);
return final;
}
/*DOC*/ static char doc_font_size[] =
/*DOC*/ "Font.size(text) -> width, height\n"
/*DOC*/ "size of rendered text\n"
/*DOC*/ "\n"
/*DOC*/ "Computes the rendered size of the given text. The text can be\n"
/*DOC*/ "standard python text or unicode. Changing the bold and italic\n"
/*DOC*/ "attributes can change the size of the rendered text.\n"
/*DOC*/ ;
static PyObject* font_size(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
int w, h;
PyObject* text;
if(!PyArg_ParseTuple(args, "O", &text))
return NULL;
if(PyUnicode_Check(text))
{
PyObject* strob = PyEval_CallMethod(text, "encode", "(s)", "utf-8");
char *string = PyString_AsString(strob);
RENPY_TTF_SizeUTF8(font, string, &w, &h);
Py_DECREF(strob);
}
else if(PyString_Check(text))
{
char* string = PyString_AsString(text);
RENPY_TTF_SizeText(font, string, &w, &h);
}
else
return RAISE(PyExc_TypeError, "text must be a string or unicode");
return Py_BuildValue("(ii)", w, h);
}
static PyObject* font_set_expand(PyObject* self, PyObject* args)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
float expand;
if(!PyArg_ParseTuple(args, "f", &expand))
return NULL;
RENPY_TTF_SetExpand(font, expand);
Py_RETURN_NONE;
}
static PyMethodDef font_methods[] =
{
{ "get_height", font_get_height, 1, doc_font_get_height },
{ "get_descent", font_get_descent, 1, doc_font_get_descent },
{ "get_ascent", font_get_ascent, 1, doc_font_get_ascent },
{ "get_linesize", font_get_linesize, 1, doc_font_get_linesize },
{ "get_bold", font_get_bold, 1, doc_font_get_bold },
{ "set_bold", font_set_bold, 1, doc_font_set_bold },
{ "get_italic", font_get_italic, 1, doc_font_get_italic },
{ "set_italic", font_set_italic, 1, doc_font_set_italic },
{ "get_underline", font_get_underline, 1, doc_font_get_underline },
{ "set_underline", font_set_underline, 1, doc_font_set_underline },
{ "render", font_render, 1, doc_font_render },
{ "size", font_size, 1, doc_font_size },
{ "set_expand", font_set_expand, 1, ""},
{ NULL, NULL }
};
/*font object internals*/
static void font_dealloc(PyFontObject* self)
{
RENPY_TTF_Font* font = PyFont_AsFont(self);
if(font && font_initialized)
RENPY_TTF_CloseFont(font);
if(self->weakreflist)
PyObject_ClearWeakRefs((PyObject*)self);
self->ob_type->tp_free((PyObject*)self);
}
static int font_init(PyFontObject *self, PyObject *args, PyObject *kwds)
{
int index = 0;
int fontsize;
RENPY_TTF_Font* font = NULL;
PyObject* fileobj;
self->font = NULL;
if(!PyArg_ParseTuple(args, "Oi|i", &fileobj, &fontsize, &index))
return -1;
if(!font_initialized)
{
RAISE(PyExc_SDLError, "font not initialized");
return -1;
}
Py_INCREF(fileobj);
if(fontsize <= 1)
fontsize = 1;
if(fileobj == Py_None) {
Py_DECREF(fileobj);
fileobj = font_resource(font_defaultname);
if(!fileobj)
{
RAISE(PyExc_RuntimeError, "default font not found");
return -1;
}
fontsize = (int)(fontsize * .6875);
if(fontsize <= 1)
fontsize = 1;
}
if(PyString_Check(fileobj) || PyUnicode_Check(fileobj))
{
FILE* test;
char* filename = PyString_AsString(fileobj);
Py_DECREF(fileobj);
fileobj = NULL;
if(!filename)
return -1;
/*check if it is a valid file, else SDL_ttf segfaults*/
test = fopen(filename, "rb");
if(!test)
{
if (!strcmp(filename, font_defaultname))
{
fileobj = font_resource(font_defaultname);
}
if (!fileobj) {
PyErr_SetString(PyExc_IOError, "unable to read font filename");
return -1;
}
}
else
{
fclose(test);
// Py_BEGIN_ALLOW_THREADS
font = RENPY_TTF_OpenFont(filename, fontsize);
// Py_END_ALLOW_THREADS
}
}
if (!font)
{
#ifdef RENPY_TTF_MAJOR_VERSION
SDL_RWops *rw;
rw = RWopsFromPython(fileobj);
if (!rw) {
Py_DECREF(fileobj);
return -1;
}
// Py_BEGIN_ALLOW_THREADS
font = RENPY_TTF_OpenFontIndexRW(rw, 1, fontsize, index);
// Py_END_ALLOW_THREADS
#else
Py_DECREF(fileobj);
RAISE(PyExc_NotImplementedError, "nonstring fonts require SDL_ttf-2.0.6");
return -1;
#endif
}
if(!font)
{
RAISE(PyExc_RuntimeError, SDL_GetError());
return -1;
}
self->font = font;
return 0;
}
/*DOC*/ static char doc_Font_MODULE[] =
/*DOC*/ "The font object is created only from pygame.font.Font(). Once a\n"
/*DOC*/ "font is created it's size and TTF file cannot be changed. The\n"
/*DOC*/ "Font objects are mainly used to render() text into a new Surface.\n"
/*DOC*/ "The Font objects also have a few states that can be set with\n"
/*DOC*/ "set_underline(bool), set_bold(bool), set_italic(bool). Each of\n"
/*DOC*/ "these functions contains an equivalent get_XXX() routine to find\n"
/*DOC*/ "the current state. There are also many routines to query the\n"
/*DOC*/ "dimensions of the text. The rendering functions work with both\n"
/*DOC*/ "normal python strings, as well as with unicode strings.\n"
/*DOC*/ ;
static PyTypeObject PyFont_Type =
{
PyObject_HEAD_INIT(NULL)
0,
"pygame.font.Font",
sizeof(PyFontObject),
0,
(destructor)font_dealloc,
0,
0, /*getattr*/
0,
0,
0,
0,
NULL,
0,
(hashfunc)NULL,
(ternaryfunc)NULL,
(reprfunc)NULL,
0L,0L,0L,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
doc_Font_MODULE, /* Documentation string */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
offsetof(PyFontObject, weakreflist), /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
font_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)font_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
//PyType_GenericNew, /* tp_new */
/*font module methods*/
/*DOC*/ static char doc_get_default_font[] =
/*DOC*/ "pygame.font.get_default_font() -> string\n"
/*DOC*/ "get the name of the default font\n"
/*DOC*/ "\n"
/*DOC*/ "returns the filename for the default truetype font.\n"
/*DOC*/ ;
static PyObject* get_default_font(PyObject* self, PyObject* args)
{
if(!PyArg_ParseTuple(args, ""))
return NULL;
return PyString_FromString(font_defaultname);
}
/*font module methods*/
#if 0
/*DOC*/ static char doc_Font[] =
/*DOC*/ "pygame.font.Font(file, size) -> Font\n"
/*DOC*/ "create a new font object\n"
/*DOC*/ "\n"
/*DOC*/ "This will create a new font object. The given file can\n"
/*DOC*/ "be a filename or any python file-like object.\n"
/*DOC*/ "The size represents the height of the font in\n"
/*DOC*/ "pixels. The file argument can be 'None', which will\n"
/*DOC*/ "use a plain default font.\n"
/*DOC*/ "\n"
/*DOC*/ "You must have at least SDL_ttf-2.0.6 for file object\n"
/*DOC*/ "support. You can load TTF and FON fonts.\n"
/*DOC*/ ;
#endif
static PyMethodDef font_builtins[] =
{
{ "__PYGAMEinit__", font_autoinit, 1, doc_init },
{ "init", fontmodule_init, 1, doc_init },
{ "quit", fontmodule_quit, 1, doc_quit },
{ "get_init", get_init, 1, doc_get_init },
{ "get_default_font", get_default_font, 1, doc_get_default_font },
{ NULL, NULL }
};
static PyObject* PyFont_New(RENPY_TTF_Font* font)
{
PyFontObject* fontobj;
if(!font)
return RAISE(PyExc_RuntimeError, "unable to load font.");
fontobj = (PyFontObject *)PyFont_Type.tp_new(&PyFont_Type, NULL, NULL);
if(fontobj)
fontobj->font = font;
return (PyObject*)fontobj;
}
/*DOC*/ static char doc_pygame_font_MODULE[] =
/*DOC*/ "The font module allows for rendering TrueType fonts into a new\n"
/*DOC*/ "Surface object. This module is optional and requires SDL_ttf as a\n"
/*DOC*/ "dependency. You may want to check for pygame.font to import and\n"
/*DOC*/ "initialize before attempting to use the module.\n"
/*DOC*/ "\n"
/*DOC*/ "Most of the work done with fonts are done by using the actual\n"
/*DOC*/ "Font objects. The module by itself only has routines to\n"
/*DOC*/ "initialize the module and create Font objects with\n"
/*DOC*/ "pygame.font.Font().\n"
/*DOC*/ "\n"
/*DOC*/ "You can load fonts from the standard system fonts by using the\n"
/*DOC*/ "pygame.font.SysFont() method. There are also other functions to\n"
/*DOC*/ "help you work with system fonts.\n"
/*DOC*/ "\n"
/*DOC*/ ;
PYGAME_EXPORT
void init_renpy_font(void)
{
PyObject *module;
if (PyType_Ready(&PyFont_Type) < 0)
return;
/* create the module */
PyFont_Type.ob_type = &PyType_Type;
PyFont_Type.tp_new = &PyType_GenericNew;
module = Py_InitModule3("_renpy_font", font_builtins, doc_pygame_font_MODULE);
self_module = module;
Py_INCREF((PyObject*)&PyFont_Type);
PyModule_AddObject(module, "FontType", (PyObject *)&PyFont_Type);
Py_INCREF((PyObject*)&PyFont_Type);
PyModule_AddObject(module, "Font", (PyObject *)&PyFont_Type);
import_pygame_base();
import_pygame_surface();
import_pygame_rwobject();
}
-1796
View File
File diff suppressed because it is too large Load Diff
-236
View File
@@ -1,236 +0,0 @@
/*
SDL_ttf: A companion library to SDL for working with TrueType (tm) fonts
Copyright (C) 1997-2004 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
/* $Id: SDL_ttf.h 2387 2006-05-11 09:03:37Z slouken $ */
/* This library is a wrapper around the excellent FreeType 2.0 library,
available at:
http://www.freetype.org/
*/
#ifndef _RENPY_TTF_H
#define _RENPY_TTF_H
#include "SDL.h"
#include "begin_code.h"
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
*/
#define RENPY_RENPY_TTF_MAJOR_VERSION 2
#define RENPY_RENPY_TTF_MINOR_VERSION 0
#define RENPY_RENPY_TTF_PATCHLEVEL 8
/* This macro can be used to fill a version structure with the compile-time
* version of the SDL_ttf library.
*/
#define RENPY_RENPY_TTF_VERSION(X) \
{ \
(X)->major = RENPY_RENPY_TTF_MAJOR_VERSION; \
(X)->minor = RENPY_RENPY_TTF_MINOR_VERSION; \
(X)->patch = RENPY_RENPY_TTF_PATCHLEVEL; \
}
/* Backwards compatibility */
#define RENPY_TTF_MAJOR_VERSION RENPY_RENPY_TTF_MAJOR_VERSION
#define RENPY_TTF_MINOR_VERSION RENPY_RENPY_TTF_MINOR_VERSION
#define RENPY_TTF_PATCHLEVEL RENPY_RENPY_TTF_PATCHLEVEL
#define RENPY_TTF_VERSION(X) RENPY_RENPY_TTF_VERSION(X)
/* This function gets the version of the dynamically linked SDL_ttf library.
it should NOT be used to fill a version structure, instead you should
use the RENPY_RENPY_TTF_VERSION() macro.
*/
extern DECLSPEC const SDL_version * SDLCALL RENPY_TTF_Linked_Version(void);
/* ZERO WIDTH NO-BREAKSPACE (Unicode byte order mark) */
#define UNICODE_BOM_NATIVE 0xFEFF
#define UNICODE_BOM_SWAPPED 0xFFFE
/* This function tells the library whether UNICODE text is generally
byteswapped. A UNICODE BOM character in a string will override
this setting for the remainder of that string.
*/
extern DECLSPEC void SDLCALL RENPY_TTF_ByteSwappedUNICODE(int swapped);
/* The internal structure containing font information */
typedef struct _RENPY_TTF_Font RENPY_TTF_Font;
/* Initialize the TTF engine - returns 0 if successful, -1 on error */
extern DECLSPEC int SDLCALL RENPY_TTF_Init(void);
/* Open a font file and create a font of the specified point size.
* Some .fon fonts will have several sizes embedded in the file, so the
* point size becomes the index of choosing which size. If the value
* is too high, the last indexed size will be the default. */
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFont(const char *file, int ptsize);
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontIndex(const char *file, int ptsize, long index);
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
extern DECLSPEC RENPY_TTF_Font * SDLCALL RENPY_TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);
/* Set and retrieve the font style
This font style is implemented by modifying the font glyphs, and
doesn't reflect any inherent properties of the truetype font file.
*/
#define RENPY_TTF_STYLE_NORMAL 0x00
#define RENPY_TTF_STYLE_BOLD 0x01
#define RENPY_TTF_STYLE_ITALIC 0x02
#define RENPY_TTF_STYLE_UNDERLINE 0x04
extern DECLSPEC int SDLCALL RENPY_TTF_GetFontStyle(RENPY_TTF_Font *font);
extern DECLSPEC void SDLCALL RENPY_TTF_SetFontStyle(RENPY_TTF_Font *font, int style);
/* Get the total height of the font - usually equal to point size */
extern DECLSPEC int SDLCALL RENPY_TTF_FontHeight(RENPY_TTF_Font *font);
/* Get the offset from the baseline to the top of the font
This is a positive value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL RENPY_TTF_FontAscent(RENPY_TTF_Font *font);
/* Get the offset from the baseline to the bottom of the font
This is a negative value, relative to the baseline.
*/
extern DECLSPEC int SDLCALL RENPY_TTF_FontDescent(RENPY_TTF_Font *font);
/* Get the recommended spacing between lines of text for this font */
extern DECLSPEC int SDLCALL RENPY_TTF_FontLineSkip(RENPY_TTF_Font *font);
/* Get the number of faces of the font */
extern DECLSPEC long SDLCALL RENPY_TTF_FontFaces(RENPY_TTF_Font *font);
/* Get the font face attributes, if any */
extern DECLSPEC int SDLCALL RENPY_TTF_FontFaceIsFixedWidth(RENPY_TTF_Font *font);
extern DECLSPEC char * SDLCALL RENPY_TTF_FontFaceFamilyName(RENPY_TTF_Font *font);
extern DECLSPEC char * SDLCALL RENPY_TTF_FontFaceStyleName(RENPY_TTF_Font *font);
/* Get the metrics (dimensions) of a glyph
To understand what these metrics mean, here is a useful link:
http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
*/
extern DECLSPEC int SDLCALL RENPY_TTF_GlyphMetrics(RENPY_TTF_Font *font, Uint16 ch,
int *minx, int *maxx,
int *miny, int *maxy, int *advance);
/* Get the dimensions of a rendered string of text */
extern DECLSPEC int SDLCALL RENPY_TTF_SizeText(RENPY_TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL RENPY_TTF_SizeUTF8(RENPY_TTF_Font *font, const char *text, int *w, int *h);
extern DECLSPEC int SDLCALL RENPY_TTF_SizeUNICODE(RENPY_TTF_Font *font, const Uint16 *text, int *w, int *h);
/* Create an 8-bit palettized surface and render the given text at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Solid(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Solid(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Solid(RENPY_TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given glyph at
fast quality with the given font and color. The 0 pixel is the
colorkey, giving a transparent background, and the 1 pixel is set
to the text color. The glyph is rendered without any padding or
centering in the X direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Solid(RENPY_TTF_Font *font,
Uint16 ch, SDL_Color fg);
/* Create an 8-bit palettized surface and render the given text at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Shaded(RENPY_TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Shaded(RENPY_TTF_Font *font,
const char *text, SDL_Color fg, SDL_Color bg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Shaded(RENPY_TTF_Font *font,
const Uint16 *text, SDL_Color fg, SDL_Color bg);
/* Create an 8-bit palettized surface and render the given glyph at
high quality with the given font and colors. The 0 pixel is background,
while other pixels have varying degrees of the foreground color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Shaded(RENPY_TTF_Font *font,
Uint16 ch, SDL_Color fg, SDL_Color bg);
/* Create a 32-bit ARGB surface and render the given text at high quality,
using alpha blending to dither the font with the given color.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderText_Blended(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUTF8_Blended(RENPY_TTF_Font *font,
const char *text, SDL_Color fg);
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderUNICODE_Blended(RENPY_TTF_Font *font,
const Uint16 *text, SDL_Color fg);
/* Create a 32-bit ARGB surface and render the given glyph at high quality,
using alpha blending to dither the font with the given color.
The glyph is rendered without any padding or centering in the X
direction, and aligned normally in the Y direction.
This function returns the new surface, or NULL if there was an error.
*/
extern DECLSPEC SDL_Surface * SDLCALL RENPY_TTF_RenderGlyph_Blended(RENPY_TTF_Font *font,
Uint16 ch, SDL_Color fg);
/* For compatibility with previous versions, here are the old functions */
#define RENPY_TTF_RenderText(font, text, fg, bg) \
RENPY_TTF_RenderText_Shaded(font, text, fg, bg)
#define RENPY_TTF_RenderUTF8(font, text, fg, bg) \
RENPY_TTF_RenderUTF8_Shaded(font, text, fg, bg)
#define RENPY_TTF_RenderUNICODE(font, text, fg, bg) \
RENPY_TTF_RenderUNICODE_Shaded(font, text, fg, bg)
/* Close an opened font file */
extern DECLSPEC void SDLCALL RENPY_TTF_CloseFont(RENPY_TTF_Font *font);
/* De-initialize the TTF engine */
extern DECLSPEC void SDLCALL RENPY_TTF_Quit(void);
/* Check if the TTF engine is initialized */
extern DECLSPEC int SDLCALL RENPY_TTF_WasInit(void);
extern DECLSPEC void SDLCALL RENPY_TTF_SetExpand(RENPY_TTF_Font *, float);
/* We'll use SDL for reporting errors */
#define RENPY_TTF_SetError SDL_SetError
#define RENPY_TTF_GetError SDL_GetError
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include "close_code.h"
#endif /* _RENPY_TTF_H */
-451
View File
@@ -1,451 +0,0 @@
/*
pygame - Python Game Library
Copyright (C) 2000-2001 Pete Shinners
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Pete Shinners
pete@shinners.org
*/
/*
* Updated by Tom to separate from pygame.
*/
#define RAISE(x,y) (PyErr_SetString((x), (y)), (PyObject*)NULL)
/*
* SDL_RWops support for python objects
*/
#include <Python.h>
#include <SDL/SDL.h>
typedef struct
{
PyObject* read;
PyObject* write;
PyObject* seek;
PyObject* tell;
PyObject* close;
#ifdef WITH_THREAD
PyThreadState* thread;
#endif
}RWHelper;
static int rw_seek(SDL_RWops* context, int offset, int whence);
static int rw_read(SDL_RWops* context, void* ptr, int size, int maxnum);
static int rw_write(SDL_RWops* context, const void* ptr, int size, int maxnum);
static int rw_close(SDL_RWops* context);
#ifdef WITH_THREAD
static int rw_seek_th(SDL_RWops* context, int offset, int whence);
static int rw_read_th(SDL_RWops* context, void* ptr, int size, int maxnum);
static int rw_write_th(SDL_RWops* context, const void* ptr, int size, int maxnum);
static int rw_close_th(SDL_RWops* context);
#endif
static SDL_RWops* get_standard_rwop(PyObject* obj)
{
if(PyString_Check(obj) || PyUnicode_Check(obj))
{
int result;
char* name;
PyObject* tuple = PyTuple_New(1);
PyTuple_SET_ITEM(tuple, 0, obj);
Py_INCREF(obj);
if(!tuple) return NULL;
result = PyArg_ParseTuple(tuple, "s", &name);
Py_DECREF(tuple);
if(!result)
return NULL;
return SDL_RWFromFile(name, "rb");
}
// else if(PyFile_Check(obj))
// return SDL_RWFromFP(PyFile_AsFile(obj), 0);
return NULL;
}
static void fetch_object_methods(RWHelper* helper, PyObject* obj)
{
helper->read = helper->write = helper->seek =
helper->tell = helper->close = NULL;
if(PyObject_HasAttrString(obj, "read"))
{
helper->read = PyObject_GetAttrString(obj, "read");
if(helper->read && !PyCallable_Check(helper->read))
helper->read = NULL;
}
if(PyObject_HasAttrString(obj, "write"))
{
helper->write = PyObject_GetAttrString(obj, "write");
if(helper->write && !PyCallable_Check(helper->write))
helper->write = NULL;
}
if(PyObject_HasAttrString(obj, "seek"))
{
helper->seek = PyObject_GetAttrString(obj, "seek");
if(helper->seek && !PyCallable_Check(helper->seek))
helper->seek = NULL;
}
if(PyObject_HasAttrString(obj, "tell"))
{
helper->tell = PyObject_GetAttrString(obj, "tell");
if(helper->tell && !PyCallable_Check(helper->tell))
helper->tell = NULL;
}
if(PyObject_HasAttrString(obj, "close"))
{
helper->close = PyObject_GetAttrString(obj, "close");
if(helper->close && !PyCallable_Check(helper->close))
helper->close = NULL;
}
}
SDL_RWops* RWopsFromPython(PyObject* obj)
{
SDL_RWops* rw;
RWHelper* helper;
if(!obj)
return (SDL_RWops*) RAISE(PyExc_TypeError, "Invalid filetype object");
rw = get_standard_rwop(obj);
if(rw) return rw;
helper = PyMem_New(RWHelper, 1);
fetch_object_methods(helper, obj);
rw = SDL_AllocRW();
rw->hidden.unknown.data1 = (void*)helper;
rw->seek = rw_seek;
rw->read = rw_read;
rw->write = rw_write;
rw->close = rw_close;
return rw;
}
static int RWopsCheckPython(SDL_RWops* rw)
{
return rw->close == rw_close;
}
static int rw_seek(SDL_RWops* context, int offset, int whence)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
if(!helper->seek || !helper->tell)
return -1;
if(!(offset == 0 && whence == SEEK_CUR)) /*being called only for 'tell'*/
{
result = PyObject_CallFunction(helper->seek, "ii", offset, whence);
if(!result)
return -1;
Py_DECREF(result);
}
result = PyObject_CallFunction(helper->tell, NULL);
if(!result)
return -1;
retval = PyInt_AsLong(result);
Py_DECREF(result);
return retval;
}
static int rw_read(SDL_RWops* context, void* ptr, int size, int maxnum)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
if(!helper->read)
return -1;
result = PyObject_CallFunction(helper->read, "i", size * maxnum);
if(!result)
return -1;
if(!PyString_Check(result))
{
Py_DECREF(result);
return -1;
}
retval = PyString_GET_SIZE(result);
memcpy(ptr, PyString_AsString(result), retval);
retval /= size;
Py_DECREF(result);
return retval;
}
static int rw_write(SDL_RWops* context, const void* ptr, int size, int num)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
if(!helper->write)
return -1;
result = PyObject_CallFunction(helper->write, "s#", ptr, size * num);
if(!result)
return -1;
Py_DECREF(result);
return num;
}
static int rw_close(SDL_RWops* context)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval = 0;
if(helper->close)
{
result = PyObject_CallFunction(helper->close, NULL);
if(result)
retval = -1;
Py_XDECREF(result);
}
Py_XDECREF(helper->seek);
Py_XDECREF(helper->tell);
Py_XDECREF(helper->write);
Py_XDECREF(helper->read);
Py_XDECREF(helper->close);
PyMem_Del(helper);
SDL_FreeRW(context);
return retval;
}
SDL_RWops* RWopsFromPythonThreaded(PyObject* obj)
{
SDL_RWops* rw;
RWHelper* helper;
PyInterpreterState* interp;
PyThreadState* thread;
if(!obj)
return (SDL_RWops*)RAISE(PyExc_TypeError, "Invalid filetype object");
rw = get_standard_rwop(obj);
if(rw)
return rw;
#ifndef WITH_THREAD
return (SDL_RWops*)RAISE(PyExc_NotImplementedError, "Python built without thread support");
#else
helper = PyMem_New(RWHelper, 1);
fetch_object_methods(helper, obj);
rw = SDL_AllocRW();
rw->hidden.unknown.data1 = (void*)helper;
rw->seek = rw_seek_th;
rw->read = rw_read_th;
rw->write = rw_write_th;
rw->close = rw_close_th;
PyEval_InitThreads();
thread = PyThreadState_Get();
interp = thread->interp;
helper->thread = PyThreadState_New(interp);
return rw;
#endif
}
static int RWopsCheckPythonThreaded(SDL_RWops* rw)
{
#ifdef WITH_THREAD
return rw->close == rw_close_th;
#else
return 0;
#endif
}
#ifdef WITH_THREAD
static int rw_seek_th(SDL_RWops* context, int offset, int whence)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
PyThreadState* oldstate;
if(!helper->seek || !helper->tell)
return -1;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
if(!(offset == 0 && whence == SEEK_CUR)) /*being called only for 'tell'*/
{
result = PyObject_CallFunction(helper->seek, "ii", offset, whence);
if(!result) {
PyErr_Clear();
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
Py_DECREF(result);
}
result = PyObject_CallFunction(helper->tell, NULL);
if(!result) {
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
retval = PyInt_AsLong(result);
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return retval;
}
static int rw_read_th(SDL_RWops* context, void* ptr, int size, int maxnum)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval;
PyThreadState* oldstate;
if(!helper->read)
return -1;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
result = PyObject_CallFunction(helper->read, "i", size * maxnum);
if(!result) {
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
if(!PyString_Check(result))
{
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
retval = PyString_GET_SIZE(result);
memcpy(ptr, PyString_AsString(result), retval);
retval /= size;
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return retval;
}
static int rw_write_th(SDL_RWops* context, const void* ptr, int size, int num)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
PyThreadState* oldstate;
if(!helper->write)
return -1;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
result = PyObject_CallFunction(helper->write, "s#", ptr, size * num);
if(!result) {
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return -1;
}
Py_DECREF(result);
PyThreadState_Swap(oldstate);
PyEval_ReleaseLock();
return num;
}
static int rw_close_th(SDL_RWops* context)
{
RWHelper* helper = (RWHelper*)context->hidden.unknown.data1;
PyObject* result;
int retval = 0;
PyThreadState* oldstate;
PyEval_AcquireLock();
oldstate = PyThreadState_Swap(helper->thread);
if(helper->close)
{
result = PyObject_CallFunction(helper->close, NULL);
if(result)
retval = -1;
Py_XDECREF(result);
}
PyThreadState_Swap(oldstate);
PyThreadState_Clear(helper->thread);
PyThreadState_Delete(helper->thread);
Py_XDECREF(helper->seek);
Py_XDECREF(helper->tell);
Py_XDECREF(helper->write);
Py_XDECREF(helper->read);
Py_XDECREF(helper->close);
PyMem_Del(helper);
PyEval_ReleaseLock();
SDL_FreeRW(context);
return retval;
}
#endif
+17 -15
View File
@@ -37,6 +37,7 @@ library("png")
library("avformat")
library("avcodec")
library("avutil")
has_avresample = library("avresample", optional=True)
has_swscale = library("swscale", optional=True)
library("freetype")
has_fribidi = library("fribidi", optional=True)
@@ -46,7 +47,7 @@ has_libglew32 = library("glew32", optional=True)
has_angle = windows and library("EGL", optional=True) and library("GLESv2", optional=True)
if android:
sdl = [ 'sdl', 'GLESv1_CM', 'log' ]
sdl = [ 'sdl', 'GLESv2', 'log' ]
else:
sdl = [ 'SDL' ]
@@ -54,15 +55,9 @@ else:
# Modules directory.
cython(
"_renpy",
[ "IMG_savepng.c", "core.c", "rwobject.c", "subpixel.c"],
[ "IMG_savepng.c", "core.c", "subpixel.c"],
sdl + [ 'png', 'z', 'm' ])
cmodule(
"_renpy_font",
[ "renpy_ttf.c", "renpy_font.c"],
sdl + [ 'freetype', 'z', 'm' ],
)
if has_fribidi and not android:
cython(
"_renpybidi",
@@ -75,22 +70,29 @@ pymodule("pysdlsound.__init__")
if not android:
sound = [ "avformat", "avcodec", "avutil", "z" ]
macros = [ ]
if has_avresample:
sound.insert(0, "avresample")
macros.append(("HAS_RESAMPLE", 1))
if has_swscale:
sound.insert(0, "swscale")
cython(
"pysdlsound.sound",
[ "pss.c", "rwobject.c", "ffdecode.c" ],
libs = sdl + sound)
[ "pss.c", "ffdecode.c" ],
libs = sdl + sound,
define_macros=macros)
# Display.
cython("renpy.display.render", libs=[ 'z', 'm' ])
cython("renpy.display.accelerator", libs=[ 'z', 'm' ])
cython("renpy.display.accelerator", libs=sdl + [ 'z', 'm' ])
# Gl.
if android:
glew_libs = [ 'GLESv1_CM', 'z', 'm' ]
glew_libs = [ 'GLESv2', 'z', 'm' ]
elif has_libglew:
glew_libs = [ 'GLEW' ]
else:
@@ -98,9 +100,9 @@ else:
cython("renpy.gl.gldraw", libs=glew_libs )
cython("renpy.gl.gltexture", libs=glew_libs)
cython("renpy.gl.glenviron_shader", libs=glew_libs)
cython("renpy.gl.glenviron_fixed", libs=glew_libs, compile_if=not android)
cython("renpy.gl.glenviron_shader", libs=glew_libs, compile_if=not android)
cython("renpy.gl.glenviron_limited", libs=glew_libs)
cython("renpy.gl.glenviron_limited", libs=glew_libs, compile_if=not android)
cython("renpy.gl.glrtt_copy", libs=glew_libs)
cython("renpy.gl.glrtt_fbo", libs=glew_libs)
+13 -12
View File
@@ -109,10 +109,11 @@ void free_gsubtable(TTGSUBTable *table)
TSingleSubstFormat *subt = lup[i].SubTable;
for(j = 0; j < ls_cnt; j++)
{
if(subt[j].Coverage.CoverageFormat == 1)
if(subt[j].Coverage.CoverageFormat == 1) {
free(subt[j].Coverage.GlyphArray);
else if(subt[j].Coverage.CoverageFormat == 2)
free(subt[j].Coverage.RangeRecord);
} else if(subt[j].Coverage.CoverageFormat == 2) {
free(subt[j].Coverage.RangeRecord);
}
if(subt[j].SubstFormat == 2)
free(subt[j].Substitute);
}
@@ -267,7 +268,7 @@ void ParseScriptList(TTGSUBTable *table, FT_Bytes raw, TScriptList *rec)
rec->ScriptRecord = NULL;
return;
}
rec->ScriptRecord = malloc(sizeof(TScriptRecord) * rec->ScriptCount);
rec->ScriptRecord = calloc(rec->ScriptCount, sizeof(TScriptRecord));
for(i = 0; i < rec->ScriptCount; i++)
{
rec->ScriptRecord[i].ScriptTag = GetUInt32(&sp);
@@ -287,7 +288,7 @@ void ParseScript(TTGSUBTable *table, FT_Bytes raw, TScript *rec)
rec->LangSysRecord = NULL;
return;
}
rec->LangSysRecord = malloc(sizeof(TLangSysRecord) * rec->LangSysCount);
rec->LangSysRecord = calloc(rec->LangSysCount, sizeof(TLangSysRecord));
for(i = 0; i < rec->LangSysCount; i++)
{
rec->LangSysRecord[i].LangSysTag = GetUInt32(&sp);
@@ -317,7 +318,7 @@ void ParseFeatureList(TTGSUBTable *table, FT_Bytes raw, TFeatureList *rec)
rec->FeatureRecord = NULL;
return;
}
rec->FeatureRecord = malloc(sizeof(TFeatureRecord) * rec->FeatureCount);
rec->FeatureRecord = calloc(rec->FeatureCount, sizeof(TFeatureRecord));
for(i = 0; i < rec->FeatureCount; i++)
{
rec->FeatureRecord[i].FeatureTag = GetUInt32(&sp);
@@ -336,7 +337,7 @@ void ParseFeature(TTGSUBTable *table, FT_Bytes raw, TFeature *rec)
{
return;
}
rec->LookupListIndex = malloc(sizeof(uint16_t) * rec->LookupCount);
rec->LookupListIndex = calloc(rec->LookupCount, sizeof(uint16_t));
for(i = 0;i < rec->LookupCount; i++)
{
rec->LookupListIndex[i] = GetUInt16(&sp);
@@ -353,7 +354,7 @@ void ParseLookupList(TTGSUBTable *table, FT_Bytes raw, TLookupList *rec)
rec->Lookup = NULL;
return;
}
rec->Lookup = malloc(sizeof(TLookup) * rec->LookupCount);
rec->Lookup = calloc(rec->LookupCount, sizeof(TLookup));
for(i = 0; i < rec->LookupCount; i++)
{
uint16_t offset = GetUInt16(&sp);
@@ -373,7 +374,7 @@ void ParseLookup(TTGSUBTable *table, FT_Bytes raw, TLookup *rec)
rec->SubTable = NULL;
return;
}
rec->SubTable = malloc(sizeof(TSingleSubstFormat) * rec->SubTableCount);
rec->SubTable = calloc(rec->SubTableCount, sizeof(TSingleSubstFormat));
if(rec->LookupType != 1)
return;
for(i = 0; i < rec->SubTableCount; i++)
@@ -413,7 +414,7 @@ void ParseCoverageFormat1(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec
rec->GlyphArray = NULL;
return;
}
rec->GlyphArray = malloc(sizeof(uint16_t) * rec->GlyphCount);
rec->GlyphArray = calloc(rec->GlyphCount, sizeof(uint16_t));
for(i = 0; i < rec->GlyphCount; i++)
{
rec->GlyphArray[i] = GetUInt16(&sp);
@@ -431,7 +432,7 @@ void ParseCoverageFormat2(TTGSUBTable *table, FT_Bytes raw, TCoverageFormat *rec
rec->RangeRecord = NULL;
return;
}
rec->RangeRecord = malloc(sizeof(TRangeRecord) * rec->RangeCount);
rec->RangeRecord = calloc(rec->RangeCount, sizeof(TRangeRecord));
for(i = 0; i < rec->RangeCount; i++)
{
rec->RangeRecord[i].Start = GetUInt16(&sp);
@@ -481,7 +482,7 @@ void ParseSingleSubstFormat2(TTGSUBTable *table, FT_Bytes raw, TSingleSubstForma
rec->Substitute = NULL;
return;
}
rec->Substitute = malloc(sizeof(uint16_t) * rec->GlyphCount);
rec->Substitute = calloc(rec->GlyphCount, sizeof(uint16_t));
for(i = 0; i < rec->GlyphCount; i++)
{
rec->Substitute[i] = GetUInt16(&sp);
+26 -6
View File
@@ -1,5 +1,9 @@
#!/usr/bin/env python
#@PydevCodeAnalysisIgnore
# This file is part of Ren'Py. The license below applies to Ren'Py only.
# Games and other projects that use Ren'Py may use a different license.
# Copyright 2004-2012 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
@@ -37,6 +41,26 @@ def path_to_common(renpy_base):
def path_to_saves(gamedir):
import renpy #@UnresolvedImport
# Android.
if renpy.android:
paths = [
os.path.join(os.environ["ANDROID_OLD_PUBLIC"], "game/saves"),
os.path.join(os.environ["ANDROID_PRIVATE"], "saves"),
os.path.join(os.environ["ANDROID_PUBLIC"], "saves"),
]
for rv in paths:
if os.path.isdir(rv):
break
print "Using savedir", rv
# We return the last path as the default.
return rv
# No save directory given.
if not renpy.config.save_directory:
return gamedir + "/saves"
@@ -54,10 +78,7 @@ def path_to_saves(gamedir):
path = newpath
# Otherwise, put the saves in a platform-specific location.
if renpy.android:
return gamedir + "/saves"
elif renpy.macintosh:
if renpy.macintosh:
rv = "~/Library/RenPy/" + renpy.config.save_directory
return os.path.expanduser(rv)
@@ -76,7 +97,7 @@ def path_to_saves(gamedir):
# Returns the path to the Ren'Py base directory (containing common and
# the launcher, usually.)
def path_to_renpy_base():
renpy_base = os.path.dirname(sys.argv[0])
renpy_base = os.path.dirname(os.path.realpath(sys.argv[0]))
renpy_base = os.environ.get('RENPY_BASE', renpy_base)
renpy_base = os.path.abspath(renpy_base)
@@ -107,7 +128,6 @@ if android:
__main__.path_to_common = path_to_common
__main__.path_to_saves = path_to_saves
os.environ["RENPY_RENDERER"] = "gl"
os.environ["RENPY_GL_ENVIRON"] = "limited"
def main():
+2 -2
View File
@@ -34,9 +34,9 @@ except ImportError:
# The tuple giving the version. This needs to be updated when
# we bump the version.
#
# Be sure to change script_version in launcher/script_version.rpy.
# Be sure to change script_version in launcher/game/script_version.rpy.
# Be sure to change config.version in tutorial/game/options.rpy.
version_tuple = (6, 15, 0, vc_version)
version_tuple = (6, 15, 7, vc_version)
# A verbose string computed from that version.
version = "Ren'Py " + ".".join(str(i) for i in version_tuple)
+12 -4
View File
@@ -278,7 +278,7 @@ class Node(object):
be executed after this one.
"""
assert False, "Node subclass forgot to define execute."
raise Exception("Node subclass forgot to define execute.")
def early_execute(self):
"""
@@ -852,7 +852,7 @@ class Transform(Node):
setattr(renpy.store, self.varname, trans)
def predict_imspec(imspec, scene=False):
def predict_imspec(imspec, scene=False, atl=None):
"""
Call this to use the given callback to predict the image named
in imspec.
@@ -888,9 +888,17 @@ def predict_imspec(imspec, scene=False):
renpy.game.context().images.predict_scene(layer)
renpy.game.context().images.predict_show(tag or name, layer)
if atl is not None:
try:
img = renpy.display.motion.ATLTransform(atl, child=img)
except:
import traceback
traceback.print_exc()
renpy.display.predict.displayable(img)
def show_imspec(imspec, atl=None):
@@ -955,7 +963,7 @@ class Show(Node):
show_imspec(self.imspec, atl=getattr(self, "atl", None))
def predict(self):
predict_imspec(self.imspec)
predict_imspec(self.imspec, atl=getattr(self, "atl", None))
return [ self.next ]
@@ -1001,7 +1009,7 @@ class Scene(Node):
def predict(self):
if self.imspec:
predict_imspec(self.imspec, scene=True)
predict_imspec(self.imspec, atl=getattr(self, "atl", None), scene=True)
return [ self.next ]
+1
View File
@@ -75,6 +75,7 @@ PROPERTIES = {
"yzoom" : float,
"zoom" : float,
"alpha" : float,
"additive" : float,
"around" : (position, position),
"alignaround" : (float, float),
"angle" : float,
+36 -13
View File
@@ -87,12 +87,15 @@ class NullFile(io.IOBase):
raise IOError("Not implemented.")
def null_files():
if sys.stderr.fileno() < 0:
sys.stderr = NullFile()
try:
if sys.stderr.fileno() < 0:
sys.stderr = NullFile()
if sys.stdout.fileno() < 0:
sys.stdout = NullFile()
except:
pass
if sys.stdout.fileno() < 0:
sys.stdout = NullFile()
null_files()
@@ -250,6 +253,14 @@ this program do not contain : or ; in their names.
renpy.config.basedir = basedir
renpy.config.gamedir = gamedir
renpy.config.args = [ ]
if renpy.android:
renpy.config.logdir = os.environ['ANDROID_PUBLIC']
else:
renpy.config.logdir = basedir
if not os.path.exists(renpy.config.logdir):
os.makedirs(renpy.config.logdir, 0777)
renpy.main.main()
keep_running = False
@@ -370,7 +381,11 @@ def open_error_file(fn, mode):
was opened.
"""
import tempfile
try:
f = file(os.path.join(renpy.config.logdir, fn), mode)
return f, fn
except:
pass
try:
f = file(fn, mode)
@@ -378,6 +393,8 @@ def open_error_file(fn, mode):
except:
pass
import tempfile
fn = os.path.join(tempfile.gettempdir(), "renpy-" + fn)
return file(fn, mode), fn
@@ -475,13 +492,16 @@ def report_exception(e, editor=True):
return simple.decode("utf-8", "replace"), full.decode("utf-8", "replace"), traceback_fn
old_memory = { }
def memory_profile():
print "Memory Profile"
print
print "Showing all objects in memory at program termination."
print
"""
Calling this function displays the change in the number of instances of
each type of object.
"""
print "- Memory Profile ---------------------------------------------------"
import gc
gc.collect()
@@ -497,4 +517,7 @@ def memory_profile():
results.sort()
for count, ty in results:
print count, str(ty)
diff = count - old_memory.get(ty, 0)
old_memory[ty] = count
if diff:
print diff, ty
+11 -5
View File
@@ -346,7 +346,7 @@ def display_say(
checkpoint=True,
ctc_timedpause=None,
ctc_force=False):
# If we're in fast skipping mode, don't bother with say
# statements at all.
if interact and renpy.config.skipping == "fast":
@@ -768,8 +768,11 @@ class ADVCharacter(object):
sub = renpy.substitutions.substitute
if who is not None:
who_pattern = sub(self.who_prefix + "[[who]" + self.who_suffix)
who = who_pattern.replace("[who]", sub(who))
if renpy.config.new_substitutions:
who_pattern = sub(self.who_prefix + "[[who]" + self.who_suffix)
who = who_pattern.replace("[who]", sub(who))
else:
who = self.who_prefix + who + self.who_suffix
ctx = renpy.game.context()
@@ -778,8 +781,11 @@ class ADVCharacter(object):
else:
translate = True
what_pattern = sub(self.what_prefix + "[[what]" + self.what_suffix)
what = what_pattern.replace("[what]", sub(what, translate=translate))
if renpy.config.new_substitutions:
what_pattern = sub(self.what_prefix + "[[what]" + self.what_suffix)
what = what_pattern.replace("[what]", sub(what, translate=translate))
else:
what = self.what_prefix + what + self.what_suffix
# Run the add_function, to add this character to the
# things like NVL-mode.
+1 -1
View File
@@ -228,7 +228,7 @@ init -1500 python:
return False
elif renpy.context()._main_menu:
return False
elif persistent._file_page == "auto":
elif (self.page or persistent._file_page) == "auto":
return False
else:
return True
+18 -2
View File
@@ -165,8 +165,16 @@ init -1500 python:
Causes the game to begin skipping. If the game is in a menu
context, then this returns to the game. Otherwise, it just
enables skipping.
`fast`
If True, skips directly to the next menu choice.
"""
fast = False
def __init__(self, fast=False):
self.fast = fast
def __call__(self):
if not self.get_sensitive():
return
@@ -174,7 +182,15 @@ init -1500 python:
if renpy.context()._menu:
renpy.jump("_return_skipping")
else:
config.skipping = not config.skipping
if not config.skipping:
if self.fast:
config.skipping = "fast"
else:
config.skipping = "slow"
else:
config.skipping = None
renpy.restart_interaction()
def get_selected(self):
+5
View File
@@ -283,6 +283,9 @@ init -1500 python in build:
# Are we building Ren'Py?
renpy = False
# Should we exclude empty directories from the zip and tar files?
exclude_empty_directories = True
# This function is called by the json_dump command to dump the build data
# into the json file.
def dump():
@@ -301,6 +304,8 @@ init -1500 python in build:
rv["version"] = version or directory_name
rv["display_name"] = display_name or executable_name
rv["exclude_empty_directories"] = exclude_empty_directories
rv["renpy"] = renpy
return rv
+5
View File
@@ -68,6 +68,11 @@ init -1900 python:
global MoveTransition
MoveTransition = OldMoveTransition
define.move_transitions = define.old_move_transitions
define.move_transitions("move", 0.5)
define.move_transitions("ease", 0.5, _ease_time_warp, _ease_in_time_warp, _ease_out_time_warp)
if version <= (6, 14, 1):
config.key_repeat = None
+70 -1
View File
@@ -97,7 +97,6 @@ init -1400 python:
# Ease images around. These are basically cosine-warped moves.
def _ease_out_time_warp(x):
print "ZZZ"
import math
return 1.0 - math.cos(x * math.pi / 2.0)
@@ -244,8 +243,78 @@ init -1400 python:
for k, v in moves.iteritems():
setattr(store, prefix + k, v)
def old_move_transitions(prefix, delay, time_warp=None, in_time_warp=None, out_time_warp=None, old=False, layers=[ 'master' ], **kwargs):
moves = {
"" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs)),
"inright" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
enter_factory=MoveIn((1.0, None, 0.0, None), time_warp=in_time_warp, **kwargs)),
"inleft" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
enter_factory=MoveIn((0.0, None, 1.0, None), time_warp=in_time_warp, **kwargs)),
"intop" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
enter_factory=MoveIn((None, 0.0, None, 1.0), time_warp=in_time_warp, **kwargs)),
"inbottom" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
enter_factory=MoveIn((None, 1.0, None, 0.0), time_warp=in_time_warp, **kwargs)),
"outright" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
leave_factory=MoveOut((1.0, None, 0.0, None), time_warp=out_time_warp, **kwargs)),
"outleft" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
leave_factory=MoveOut((0.0, None, 1.0, None), time_warp=out_time_warp, **kwargs)),
"outtop" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
leave_factory=MoveOut((None, 0.0, None, 1.0), time_warp=out_time_warp, **kwargs)),
"outbottom" : MoveTransition(
delay,
old=old,
layers=layers,
factory=MoveFactory(time_warp=time_warp, **kwargs),
leave_factory=MoveOut((None, 1.0, None, 0.0), time_warp=time_warp, **kwargs)),
}
for k, v in moves.iteritems():
setattr(store, prefix + k, v)
define.move_transitions = move_transitions
define.old_move_transitions = old_move_transitions
del move_transitions
del old_move_transitions
define.move_transitions("move", 0.5)
define.move_transitions("ease", 0.5, _ease_time_warp, _ease_in_time_warp, _ease_out_time_warp)
+15 -4
View File
@@ -12,12 +12,12 @@ init -1500 python:
"""
The action returned by MusicRoom.Play when called with a file.
"""
def __init__(self, mr, filename):
self.mr = mr
self.filename = filename
self.selected = self.get_selected()
def __call__(self):
self.mr.play(self.filename, 0)
@@ -42,7 +42,9 @@ init -1500 python:
order.
"""
def __init__(self, channel="music", fadeout=0.0, fadein=0.0):
loop = False
def __init__(self, channel="music", fadeout=0.0, fadein=0.0, loop=False):
"""
`channel`
The channel that this music room will operate on.
@@ -54,6 +56,10 @@ init -1500 python:
`fadein`
The number of seconds it takes to fade in the new
music when changing tracks.
`loop`
If true, a music track will loop once played. If False, it
will advance to the next track.
"""
self.channel = channel
@@ -71,6 +77,8 @@ init -1500 python:
# The set of songs that are always unlocked.
self.always_unlocked = set()
# Should we loop rather than advancing to the next track?
self.loop = loop
def add(self, filename, always_unlocked=False):
"""
@@ -144,7 +152,10 @@ init -1500 python:
idx = (idx + offset) % len(playlist)
playlist = playlist[idx:] + playlist[:idx]
if self.loop:
playlist = [ playlist[idx] ]
else:
playlist = playlist[idx:] + playlist[:idx]
renpy.music.play(playlist, channel=self.channel, fadeout=self.fadeout, fadein=self.fadein)
+5 -2
View File
@@ -49,6 +49,9 @@ init -1900 python:
# Prefixes to strip from automatic images.
config.automatic_images_strip = [ ]
# The minimum number of components which the image name consists of is 2 by default.
config.automatic_images_minimum_components = 2
init 1900 python hide:
@@ -86,8 +89,8 @@ init 1900 python hide:
else:
break
# Only names of 2 components or more.
if len(name) < 2:
# Only names of 2 components or more by default.
if len(name) < config.automatic_images_minimum_components:
continue
# Reject if it already exists.
+3
View File
@@ -86,6 +86,9 @@ init -1800 python hide:
style.list_text = Style(style.default)
style.tile = Style(style.default, help='default style of tile')
# Not used - but some old games might customize it.
style.error_root = Style(style.default)
# The base styles that can be customized by themes.
+4 -1
View File
@@ -886,7 +886,10 @@ init -1500 python in updater:
# Check the existence of the downloaded file.
if not os.path.exists(new_fn):
raise UpdateError("The update file was not downloaded.")
if os.path.exists(new_fn + ".part"):
os.rename(new_fn + ".part", new_fn)
else:
raise UpdateError("The update file was not downloaded.")
# Check that the downloaded file has the right digest.
import hashlib
+19 -9
View File
@@ -1,7 +1,7 @@
# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
# See LICENSE.txt for license details.
init python hide:
init python:
style.menu_button = Style(style.button, heavy=True, help='Buttons that are part of the main or game menus.')
style.menu_button_text = Style(style.button_text, heavy=True, help='The label of buttons that are part of the main or game menus.')
@@ -43,7 +43,6 @@ init python hide:
style.yesno_button = Style(style.menu_button, heavy=True, help='A Yes/No button.')
style.yesno_button_text = Style(style.menu_button_text, heavy=True, help='A Yes/No button label.')
style.prefs_frame = Style(style.default, heavy=True, help='')
style.prefs_pref_frame = Style(style.default, heavy=True, help='')
style.prefs_pref_vbox = Style(style.thin_vbox, heavy=True, help='')
@@ -323,15 +322,25 @@ init python hide:
store._selected_compat = [ ]
class _SelectedCompat(object):
class _SelectedCompat(renpy.style.Style):
def __init__(self, target):
self.__dict__["target"] = target
self.__dict__["property_updates"] = [ ]
self.target = target
self.property_updates = [ ]
super(_SelectedCompat, self).__init__(target)
store._selected_compat.append(self)
def __setattr__(self, k, v):
def setattr(self, k, v):
super(_SelectedCompat, self).setattr(k, v)
try:
super(_SelectedCompat, self).setattr("selected_" + k, v)
except:
pass
self.property_updates.append((k, v))
def apply(self):
@@ -340,7 +349,8 @@ init python hide:
setattr(target, "selected_" + k, v)
def clear(self):
self.__dict__["property_updates"] = [ ]
super(_SelectedCompat, self).clear()
self.property_updates = [ ]
style.selected_button = _SelectedCompat('button')
@@ -350,8 +360,8 @@ init python hide:
style.prefs_selected_button = _SelectedCompat('prefs_button')
style.prefs_selected_button_text = _SelectedCompat('prefs_button_text')
def apply_selected_compat():
def _apply_selected_compat():
for scs in _selected_compat:
scs.apply()
layout.compat_funcs.append(apply_selected_compat)
layout.compat_funcs.append(_apply_selected_compat)
+1
View File
@@ -379,6 +379,7 @@ gamedir = None
basedir = None
renpy_base = None
commondir = None
logdir = None # Where log and error files go.
# Should we enable OpenGL mode?
gl_enable = True
+1
View File
@@ -108,6 +108,7 @@ Solid = renpy.display.imagelike.Solid
LiveComposite = renpy.display.layout.LiveComposite
LiveCrop = renpy.display.layout.LiveCrop
LiveTile = renpy.display.layout.LiveTile
Flatten = renpy.display.layout.Flatten
Null = renpy.display.layout.Null
Window = renpy.display.layout.Window
+37 -4
View File
@@ -22,9 +22,43 @@
import renpy
import math
from renpy.display.render cimport Render, Matrix2D, render
################################################################################
# Surface copying
################################################################################
from pygame cimport *
def nogil_copy(src, dest):
"""
Does a gil-less blit of src to dest, with minimal locking.
"""
cdef SDL_Surface *src_surf
cdef SDL_Surface *dst_surf
src_surf = PySurface_AsSurface(src)
dest_surf = PySurface_AsSurface(dest)
old_alpha = src_surf.flags & SDL_SRCALPHA
if old_alpha:
SDL_SetAlpha(src_surf, 0, 255)
with nogil:
SDL_BlitSurface(src_surf, NULL, dest_surf, NULL)
if old_alpha:
SDL_SetAlpha(src_surf, SDL_SRCALPHA, 255)
################################################################################
# Transform render function
################################################################################
cdef Matrix2D IDENTITY
IDENTITY = renpy.display.render.IDENTITY
@@ -221,8 +255,6 @@ def transform_render(self, widtho, heighto, st, at):
xo += width / 2.0
yo += height / 2.0
alpha = state.alpha
rv = Render(width, height)
# Default case - no transformation matrix.
@@ -245,7 +277,8 @@ def transform_render(self, widtho, heighto, st, at):
-rydx / inv_det,
rxdx / inv_det)
rv.alpha = alpha
rv.alpha = state.alpha
rv.over = 1.0 - state.additive
rv.clipping = clipping
pos = (xo, yo)
+5 -3
View File
@@ -1271,15 +1271,17 @@ class Bar(renpy.display.core.Displayable):
super(Bar, self).focus(default)
self.set_transform_event("hover")
run(self.hovered)
if not default:
run(self.hovered)
def unfocus(self, default=False):
super(Bar, self).unfocus()
self.set_transform_event("idle")
run_unhovered(self.hovered)
run(self.unhovered)
if not default:
run_unhovered(self.hovered)
run(self.unhovered)
def event(self, ev, x, y, st):
+60 -25
View File
@@ -213,7 +213,7 @@ class Displayable(renpy.object.Object):
in seconds.
"""
assert False, "Draw not implemented."
raise Exception("Render not implemented.")
def event(self, ev, x, y, st):
"""
@@ -1080,6 +1080,9 @@ class Interface(object):
# 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]
@@ -1366,6 +1369,7 @@ class Interface(object):
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):
@@ -1389,7 +1393,7 @@ class Interface(object):
raise Exception("Could not set video mode.")
# Save the video size.
if renpy.config.save_physical_size and not fullscreen:
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:
@@ -1408,7 +1412,7 @@ class Interface(object):
self.restart_interaction = True
def draw_screen(self, root_widget, fullscreen_video):
def draw_screen(self, root_widget, fullscreen_video, draw):
surftree = renpy.display.render.render_screen(
root_widget,
@@ -1416,8 +1420,9 @@ class Interface(object):
renpy.config.screen_height,
)
renpy.display.draw.draw_screen(surftree, fullscreen_video)
if draw:
renpy.display.draw.draw_screen(surftree, fullscreen_video)
renpy.display.render.mark_sweep()
renpy.display.focus.take_focuses()
@@ -1789,6 +1794,26 @@ class Interface(object):
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):
"""
@@ -1805,6 +1830,7 @@ class Interface(object):
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:
@@ -1868,7 +1894,7 @@ class Interface(object):
@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
@@ -1940,10 +1966,7 @@ class Interface(object):
REDRAW))
# Add a single TIMEEVENT to the queue.
try:
pygame.event.post(self.time_event)
except:
pass
self.post_time_event()
# Figure out the scene list we want to show.
scene_lists = renpy.game.context().scene_lists
@@ -2057,6 +2080,7 @@ class Interface(object):
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()
@@ -2101,6 +2125,9 @@ class Interface(object):
# 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:
@@ -2135,7 +2162,7 @@ class Interface(object):
if not self.interact_time:
self.interact_time = self.frame_time
self.draw_screen(root_widget, fullscreen_video)
self.draw_screen(root_widget, fullscreen_video, (not fullscreen_video) or video_frame_drawn)
if first_pass:
scene_lists.set_times(self.interact_time)
@@ -2152,7 +2179,6 @@ class Interface(object):
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()
@@ -2164,7 +2190,7 @@ class Interface(object):
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()
@@ -2174,6 +2200,11 @@ class Interface(object):
# 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
@@ -2190,7 +2221,10 @@ class Interface(object):
redraw_in = time_left
if time_left <= 0:
pygame.event.post(self.redraw_event)
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))
@@ -2211,23 +2245,27 @@ class Interface(object):
if time_left <= 0:
self.timeout_time = None
pygame.time.set_timer(TIMEEVENT, 0)
pygame.event.post(self.time_event)
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) \
and not needs_redraw \
and not self.event_peek() \
and not renpy.audio.music.is_playing("movie"):
while prediction_coroutine is not None:
result = prediction_coroutine.next()
# 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():
@@ -2368,10 +2406,8 @@ class Interface(object):
# An ignored event can change the timeout. So we want to
# process an TIMEEVENT to ensure that the timeout is
# set correctly.
try:
pygame.event.post(self.time_event)
except:
pass
self.post_time_event()
# Check again after handling the event.
needs_redraw |= renpy.display.render.process_redraws()
@@ -2383,7 +2419,6 @@ class Interface(object):
# 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.
+2 -2
View File
@@ -463,9 +463,9 @@ class Drag(renpy.display.core.Displayable, renpy.python.RevertableObject):
new_x = par_x - self.grab_x + xo
new_y = par_y - self.grab_y + yo
new_x = max(new_x, 0)
new_x = min(new_x, i.parent_width - i.w)
new_x = min(new_x, int(i.parent_width - i.w))
new_y = max(new_y, 0)
new_y = min(new_y, i.parent_height - i.h)
new_y = min(new_y, int(i.parent_height - i.h))
if i.drag_group is not None and i.drag_name is not None:
i.drag_group.positions[i.drag_name] = (new_x, new_y)
+2 -8
View File
@@ -294,11 +294,9 @@ def horiz_line_dist(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1):
# The right end of a is to the left of the left end of b.
if ax0 <= ax1 <= bx0 <= bx1:
return points_dist(ax1, ay1, bx0, by0, renpy.config.focus_crossrange_penalty, 1.0)
if bx0 <= bx1 <= ax0 <= ax1:
else:
return points_dist(ax0, ay0, bx1, by1, renpy.config.focus_crossrange_penalty, 1.0)
assert False
# This computes the distance between two vertical lines. (So the
# distance is either hortizontal, or has a horizontal component to it.)
@@ -316,13 +314,9 @@ def verti_line_dist(ax0, ay0, ax1, ay1, bx0, by0, bx1, by1):
# The right end of a is to the left of the left end of b.
if ay0 <= ay1 <= by0 <= by1:
return points_dist(ax1, ay1, bx0, by0, 1.0, renpy.config.focus_crossrange_penalty)
if by0 <= by1 <= ay0 <= ay1:
else:
return points_dist(ax0, ay0, bx1, by1, 1.0, renpy.config.focus_crossrange_penalty)
assert False
# This focuses the widget that is nearest to the current widget. To
# determine nearest, we compute points on the widgets using the
+65 -65
View File
@@ -31,6 +31,7 @@ import cStringIO
import threading
import time
# This is an entry in the image cache.
class CacheEntry(object):
@@ -74,9 +75,12 @@ class Cache(object):
# The total size of everything in the cache.
self.total_cache_size = 0
# A lock that must be held when updating the above.
# A lock that must be held when updating the cache.
self.lock = threading.Condition()
# A lock that mist be held to notify the preload thread.
self.preload_lock = threading.Condition()
# Is the preload_thread alive?
self.keep_preloading = True
@@ -120,16 +124,16 @@ class Cache(object):
if not self.preload_thread.isAlive():
return
self.lock.acquire()
self.keep_preloading = False
self.lock.notify()
self.lock.release()
with self.preload_lock:
self.keep_preloading = False
self.preload_lock.notify()
self.preload_thread.join()
# Clears out the cache.
def clear(self):
self.lock.acquire()
self.preloads = [ ]
@@ -179,25 +183,12 @@ class Cache(object):
renpy.display.render.mutated_surface(surf)
return surf
ce = None
# First try to grab the image out of the cache without locking it.
if image in self.cache:
ce = self.cache[image]
ce = self.cache.get(image, None)
# Now, grab the cache and try again. This deals with the case where the image
# was already in the middle of preloading.
# Otherwise, we load the image ourselves.
if ce is None:
self.lock.acquire()
ce = self.cache.get(image, None)
if ce is not None:
self.lock.release()
# Otherwise, we keep the lock, and load the image ourselves.
if ce is None:
try:
if image in self.pin_cache:
surf = self.pin_cache[image]
@@ -205,33 +196,34 @@ class Cache(object):
surf = image.load()
except:
self.lock.release()
raise
with self.lock:
ce = CacheEntry(image, surf)
if image not in self.cache:
self.total_cache_size += ce.size
ce = CacheEntry(image, surf)
self.total_cache_size += ce.size
self.cache[image] = ce
self.cache[image] = ce
# Indicate that this surface had changed.
renpy.display.render.mutated_surface(ce.surf)
if renpy.config.debug_image_cache:
if predict:
renpy.display.ic_log.write("Added %r (%.02f%%)", ce.what, 100.0 * self.total_cache_size / self.cache_limit)
else:
renpy.display.ic_log.write("Total Miss %r", ce.what)
renpy.display.draw.load_texture(ce.surf)
# Indicate that this surface had changed.
renpy.display.render.mutated_surface(ce.surf)
if renpy.config.debug_image_cache:
if predict:
renpy.display.ic_log.write("Added %r (%.02f%%)", ce.what, 100.0 * self.total_cache_size / self.cache_limit)
else:
renpy.display.ic_log.write("Total Miss %r", ce.what)
renpy.display.draw.load_texture(ce.surf)
self.lock.release()
# Move it into the current generation. This isn't protected by
# a lock, so in certain circumstances we could have an
# inaccurate size. But that's pretty unlikely, as the
# preloading thread should never run at the same time as an
# actual load from the normal thread.
# inaccurate size - but that will be cured at the end of the
# current generation.
if ce.time != self.time:
ce.time = self.time
self.size_of_current_generation += ce.size
@@ -302,31 +294,38 @@ class Cache(object):
in_cache = True
else:
self.preloads.append(im)
self.lock.notify()
in_cache = False
if not in_cache:
with self.preload_lock:
self.preload_lock.notify()
if in_cache and renpy.config.debug_image_cache:
renpy.display.ic_log.write("Kept %r", im)
def end_prediction(self):
def start_prediction(self):
"""
Called on the end of prediction, to kick of the thread so cleanup
can happen.
Called at the start of prediction, to ensure the thread runs
at least once to clean out the cache.
"""
with self.lock:
self.lock.notify()
with self.preload_lock:
self.preload_lock.notify()
def preload_thread_main(self):
while self.keep_preloading:
self.lock.acquire()
self.lock.wait()
self.lock.release()
self.preload_lock.acquire()
self.preload_lock.wait()
self.preload_lock.release()
while self.preloads and self.keep_preloading:
start = time.time()
# If the size of the current generation is bigger than the
# total cache size, stop preloading.
with self.lock:
@@ -337,24 +336,24 @@ class Cache(object):
if renpy.config.debug_image_cache:
for i in self.preloads:
renpy.display.ic_log.write("Overfull %r", i)
self.preloads = [ ]
break
try:
image = self.preloads.pop(0)
try:
image = self.preloads.pop(0)
if image not in self.preload_blacklist:
try:
self.get(image, True)
except:
self.preload_blacklist.add(image)
if image not in self.preload_blacklist:
try:
self.get(image, True)
except:
self.preload_blacklist.add(image)
except:
pass
except:
pass
self.cleanout()
with self.lock:
self.cleanout()
# If we have time, preload pinned images.
if self.keep_preloading and not renpy.game.less_memory:
@@ -459,9 +458,10 @@ class ImageBase(renpy.display.core.Displayable):
would override this.
"""
assert False
raise Exception("load method not implemented.")
def render(self, w, h, st, at):
im = cache.get(self)
texture = renpy.display.draw.load_texture(im)
+42 -3
View File
@@ -478,7 +478,7 @@ class MultiBox(Container):
if adjust_times:
it = renpy.game.interface.interact_time - st
it = renpy.game.interface.interact_time
self.start_times = [ i or it for i in self.start_times ]
self.anim_times = [ i or it for i in self.anim_times ]
@@ -1328,7 +1328,6 @@ class Viewport(Container):
value = max(cw - width, 0) * self.xoffset
self.xadjustment.value = value
self.xoffset = None
if self.yoffset is not None:
if isinstance(self.yoffset, int):
@@ -1337,7 +1336,6 @@ class Viewport(Container):
value = max(ch - height, 0) * self.yoffset
self.yadjustment.value = value
self.yoffset = None
if self.edge_size and self.edge_last_st and (self.edge_xspeed or self.edge_yspeed):
@@ -1378,6 +1376,9 @@ class Viewport(Container):
def event(self, ev, x, y, st):
self.xoffset = None
self.yoffset = None
rv = super(Viewport, self).event(ev, x, y, st)
if rv is not None:
return rv
@@ -1741,3 +1742,41 @@ class LiveTile(Container):
rv.blit(cr, (x, y), focus=False)
return rv
class Flatten(Container):
"""
:doc: disp_imagelike
This flattens `child`, which may be made up of multiple textures, into
a single texture.
Certain operations, like the alpha transform property, apply to every
texture making up a displayable, which can yield incorrect results
when the textures overlap on screen. Flatten creates a single texture
from multiple textures, which can prevent this problem.
Flatten is a relatively expensive operation, and so should only be used
when absolutely required.
"""
def __init__(self, child, **properties):
super(Flatten, self).__init__(**properties)
self.add(child)
def render(self, width, height, st, at):
cr = renpy.display.render.render(self.child, width, height, st, at)
cw, ch = cr.get_size()
tex = cr.render_to_texture(True)
rv = renpy.display.render.Render(cw, ch)
rv.blit(tex, (0, 0))
rv.depends_on(cr, focus=True)
return rv
+9 -1
View File
@@ -95,9 +95,11 @@ class TransformState(renpy.object.Object):
default_xoffset = None
default_yoffset = None
transform_anchor = False
additive = 0.0
def __init__(self): # W0231
self.alpha = 1
self.additive = 0.0
self.rotate = None
self.rotate_pad = True
self.transform_anchor = False
@@ -142,6 +144,7 @@ class TransformState(renpy.object.Object):
def take_state(self, ts):
self.alpha = ts.alpha
self.additive = ts.additive
self.rotate = ts.rotate
self.rotate_pad = ts.rotate_pad
self.transform_anchor = ts.transform_anchor
@@ -196,6 +199,7 @@ class TransformState(renpy.object.Object):
rv[prop] = (old_value, new_value)
diff2("alpha", newts.alpha, self.alpha)
diff2("additive", newts.additive, self.additive)
diff2("rotate", newts.rotate, self.rotate)
diff2("rotate_pad", newts.rotate_pad, self.rotate_pad)
diff2("transform_anchor", newts.transform_anchor, self.transform_anchor)
@@ -380,6 +384,7 @@ class Transform(Container):
# Proxying things over to our state.
alpha = Proxy("alpha")
additive = Proxy("additive")
rotate = Proxy("rotate")
rotate_pad = Proxy("rotate_pad")
transform_anchor = Proxy("rotate_pad")
@@ -514,7 +519,7 @@ class Transform(Container):
raise Exception("Unknown transform property prefix: %r" % prefix)
if prop not in renpy.atl.PROPERTIES:
raise Exception("Unknown transform property: %r")
raise Exception("Unknown transform property: %r" % prop)
self.arguments[prefix][prop] = v
@@ -725,6 +730,9 @@ class Transform(Container):
children = self.children
offsets = self.offsets
if not offsets:
return None
for i in xrange(len(self.children)-1, -1, -1):
d = children[i]
-8
View File
@@ -191,8 +191,6 @@ def OldMoveTransition(delay, old_widget=None, new_widget=None, factory=None, ent
# for each layer.
if new.layers:
assert old.layers
rv = renpy.display.layout.MultiBox(layout='fixed')
rv.layers = { }
@@ -502,12 +500,8 @@ def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=No
# for each layer.
if new.layers:
assert old.layers
rv = renpy.display.layout.MultiBox(layout='fixed')
rv.layers = { }
for layer in renpy.config.layers:
f = new.layers[layer]
@@ -518,7 +512,6 @@ def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=No
f = merge_slide(old.layers[layer], new.layers[layer])
rv.layers[layer] = f
rv.add(f)
return rv
@@ -632,7 +625,6 @@ def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=No
layer = new.layer_name
rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **renpy.game.interface.layer_properties[layer])
rv.append_scene_list(rv_sl)
rv.layer_name = layer
return rv
+2 -2
View File
@@ -261,7 +261,7 @@ class SpriteManager(renpy.display.core.Displayable):
if self.dead_child:
self.children = [ i for i in self.children if i.live ]
self.children.sort()
self.children.sort(key=lambda sc:sc.zorder)
caches = [ ]
@@ -282,7 +282,7 @@ class SpriteManager(renpy.display.core.Displayable):
cst = st - cache.st
cache.render = r = render(cache.child, width, height, cst, cst)
cache.fast = (r.operation == BLIT) and (r.forward is None) and (r.alpha == 1.0)
cache.fast = (r.operation == BLIT) and (r.forward is None) and (r.alpha == 1.0) and (r.over == 1.0)
rv.depends_on(r)
caches.append(cache)
+3 -6
View File
@@ -117,11 +117,7 @@ def copy_surface(surf, alpha=True):
"""
rv = surface_unscaled(surf.get_size(), alpha)
renpy.display.render.blit_lock.acquire()
rv.blit(surf, (0, 0))
renpy.display.render.blit_lock.release()
renpy.display.accelerator.nogil_copy(surf, rv) # @UndefinedVariable
return rv
copy_surface_unscaled = copy_surface
@@ -131,7 +127,8 @@ copy_surface_unscaled = copy_surface
def load_image(f, filename):
surf = pygame.image.load(f, renpy.exports.fsencode(filename))
return copy_surface_unscaled(surf)
rv = copy_surface_unscaled(surf)
return rv
load_image_unscaled = load_image
+23 -10
View File
@@ -71,10 +71,23 @@ def prediction_coroutine(root_widget):
The image prediction co-routine. This predicts the images that can
be loaded in the near future, and passes them to the image cache's
preload_image method to be queued up for loading.
The .send should be called with True to do a expensive prediction,
and with False to either do an inexpensive prediction or no
prediction at all.
Returns True if there's more predicting to be done, or False
if there's no more predicting worth doing.
"""
global predicting
# Wait to be told to start.
yield True
# Start the prediction thread (to clean out the cache).
renpy.display.im.cache.start_prediction()
# Set up the image prediction method.
global image
image = renpy.display.im.cache.preload_image
@@ -83,15 +96,15 @@ def prediction_coroutine(root_widget):
# clicks.
predicting = True
renpy.game.context().predict()
predicting = False
yield True
for _i in renpy.game.context().predict():
predicting = False
yield True
predicting = True
# If there's a parent context, predict we'll be returning to it
# shortly. Otherwise, call the functions in
# config.predict_callbacks.
predicting = True
if len(renpy.game.contexts) >= 2:
sls = renpy.game.contexts[-2].scene_lists
@@ -109,7 +122,8 @@ def prediction_coroutine(root_widget):
predicting = False
yield True
while not (yield True):
continue
# Predict things (especially screens) that are reachable through
# an action.
@@ -124,7 +138,8 @@ def prediction_coroutine(root_widget):
# Predict the screens themselves.
for name, args, kwargs in screens:
yield True
while not (yield True):
continue
predicting = True
@@ -136,8 +151,6 @@ def prediction_coroutine(root_widget):
renpy.display.ic_log.exception()
predicting = False
renpy.display.im.cache.end_prediction()
yield False
+1 -4
View File
@@ -50,10 +50,7 @@ def start(basedir, gamedir):
import subprocess
import sys
if sys.argv[0].lower().endswith(".exe"):
cmd = [sys.argv[0], "show", "presplash", fn]
else:
cmd = [sys.executable, "-OO", sys.argv[0], "show", "presplash", fn]
cmd = [sys.executable, "-EOO", sys.argv[0], "show", "presplash", fn]
def fsencode(s):
if isinstance(s, str):
+1
View File
@@ -24,6 +24,7 @@ cdef class Render:
cdef public Matrix2D forward, reverse
cdef public double alpha
cdef public double over
cdef public list focuses
cdef public list pass_focuses
+4
View File
@@ -508,6 +508,10 @@ cdef class Render:
# This is used to adjust the alpha of children of this render.
self.alpha = 1
# The over blending factor. When this is 1.0, we get fully additive
# blending. When set to 0.0, we get additive blending.
self.over = 1.0
# A list of focus regions in this displayable.
self.focuses = None
+6 -7
View File
@@ -249,10 +249,9 @@ def surface(w, h, alpha):
def copy_surface(surf):
w, h = surf.get_size()
rv = surface(w, h, True)
rv.blit(surf, (0, 0))
renpy.display.accelerator.nogil_copy(surf, rv) # @UndefinedVariable
return rv
def draw_special(what, dest, x, y):
@@ -479,10 +478,10 @@ def draw(dest, clip, what, xo, yo, screen):
dest = dest.subsurface((x, y, width, height))
# Deal with alpha and transforms by passing them off to draw_transformed.
if what.alpha != 1 or (what.forward is not None and what.forward is not IDENTITY):
if what.alpha != 1 or what.over != 1.0 or (what.forward is not None and what.forward is not IDENTITY):
for child, cxo, cyo, _focus, _main in what.visible_children:
draw_transformed(dest, clip, child, xo + cxo, yo + cyo,
what.alpha, what.forward, what.reverse)
what.alpha * what.over, what.forward, what.reverse)
return
for child, cxo, cyo, _focus, _main in what.visible_children:
@@ -630,7 +629,7 @@ def draw_transformed(dest, clip, what, xo, yo, alpha, forward, reverse):
child_forward = forward
child_reverse = reverse
draw_transformed(dest, clip, child, xo + cxo, yo + cyo, alpha * what.alpha, child_forward, child_reverse)
draw_transformed(dest, clip, child, xo + cxo, yo + cyo, alpha * what.alpha * what.over, child_forward, child_reverse)
@@ -696,7 +695,7 @@ class SWDraw(object):
self.fullscreen_surface = None
# Info.
self.info = { "renderer" : "sw", "resizable" : False }
self.info = { "renderer" : "sw", "resizable" : False, "additive" : False }
pygame.display.init()
renpy.display.interface.post_init()
+4
View File
@@ -127,6 +127,10 @@ def launch_editor(filenames, line=1, transient=False):
Causes the editor to be launched.
"""
# On android, we will never be able to launch the editor.
if renpy.android:
return True
if editor is None:
init()
+14 -3
View File
@@ -394,6 +394,9 @@ class Context(renpy.object.Object):
rv.runtime = self.runtime
rv.info = self.info
rv.translate_language = self.translate_language
rv.translate_identifier = self.translate_identifier
return rv
@@ -443,7 +446,11 @@ class Context(renpy.object.Object):
# We accept that sometimes prediction won't work.
self.images = old_images
self.images = old_images
yield True
yield False
def seen_current(self, ever):
@@ -500,8 +507,12 @@ def run_context(top):
label = None
context.run()
rv = renpy.store._return
context.pop_all_dynamic()
break
return rv
except renpy.game.RestartContext as e:
@@ -525,5 +536,5 @@ def run_context(top):
context.pop_all_dynamic()
raise
+37 -28
View File
@@ -218,14 +218,15 @@ def copy_images(old, new):
def showing(name, layer='master'):
"""
This returns true if an image with the same tag as that found in
the suppled image name is present on the given layer.
@param name may be a tuple of strings, or a single string. In the latter
case, it is split on whitespace to make a tuple. The first element
of the tuple is used as the image tag.
@param layer is the name of the layer.
:doc: image_func
Returns true if an image with the same tag as `name` is showing on
`layer`
`image`
May be a string giving the image name or a tuple giving each
component of the image name. It may also be a string giving
only the image tag.
"""
if not isinstance(name, tuple):
@@ -397,24 +398,28 @@ def watch(expression, style='default', **properties):
def input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None): #@ReservedAssignment
"""
This pops up a window requesting that the user enter in some text.
It returns the entered text.
:doc: input
@param prompt: A prompt that is used to ask the user for the text.
@param default: A default for the text that this input can return.
@param length: If given, a limit to the amount of text that this
function will return.
@param allow: If not None, then if an input character is not in this
string, it is ignored.
@param exclude: If not None, then if an input character is in this
set, it is ignored.
@param with_none: If True, performs a with None after the input. If None,
takes the value from config.implicit_with_none.
Calling this function pops up a window asking the player to enter some
text. It returns the entered text.
`prompt`
A string giving a prompt to display to the player.
`default`
A string giving the initial text that will be edited by the player.
`allow`
If not None, a string giving a list of characters that will
be allowed in the text.
`exclude`
If not None, if a character is present in this string, it is not
allowed in the text.
`length`
If not None, this must be an integer giving the maximum length
of the input string.
"""
renpy.exports.mode('input')
@@ -1663,9 +1668,12 @@ def get_renderer_info():
One of ``"gl"`` or ``"sw"``, corresponding to the OpenGL and
software renderers, respectively.
``"resizable``
``"resizable"``
True if and only if the window is resizable.
``"additive"``
True if and only if the renderer supports additive blending.
Other, renderer-specific, keys may also exist. The dictionary should
be treated as immutable. This should only be called once the display
has been started (that is, after the init code is finished).
@@ -1819,13 +1827,14 @@ def set_physical_size(size):
"""
:doc: other
Attempts to set the size of the physical window to size. This has the
side effect of taking the screen out of windowed mode.
Attempts to set the size of the physical window to `size`. This has the
side effect of taking the screen out of fullscreen mode.
"""
renpy.game.preferences.fullscreen = False
if get_renderer_info()["resizable"]:
renpy.display.draw.quit()
renpy.display.interface.set_mode(size)
def fsencode(s):
+10 -5
View File
@@ -315,6 +315,9 @@ def invoke_in_new_context(callable, *args, **kwargs): #@ReservedAssignment
context = renpy.execution.Context(False, contexts[-1], clear=True)
contexts.append(context)
if renpy.display.interface is not None:
renpy.display.interface.enter_context()
try:
return callable(*args, **kwargs)
@@ -346,6 +349,9 @@ def call_in_new_context(label, *args, **kwargs):
context = renpy.execution.Context(False, contexts[-1], clear=True)
contexts.append(context)
if renpy.display.interface is not None:
renpy.display.interface.enter_context()
if args:
renpy.store._args = args
@@ -360,11 +366,7 @@ def call_in_new_context(label, *args, **kwargs):
try:
context.goto_label(label)
renpy.execution.run_context(False)
rv = renpy.store._return #@UndefinedVariable
return rv
return renpy.execution.run_context(False)
except renpy.game.JumpOutException, e:
@@ -399,6 +401,9 @@ def call_replay(label, scope={}):
context = renpy.execution.Context(True)
contexts.append(context)
if renpy.display.interface is not None:
renpy.display.interface.enter_context()
for k, v in scope.iteritems():
setattr(renpy.store, k, v)
+2
View File
@@ -26,6 +26,7 @@ cdef class GLDraw:
cdef bint fast_dissolve
cdef bint always_opaque
cdef bint allow_fixed
cdef tuple default_clip
cdef public tuple clip_rtt_box
@@ -43,6 +44,7 @@ cdef class GLDraw:
double xo,
double yo,
double alpha,
double over,
render.Matrix2D reverse)
cdef class Environ:
+39 -27
View File
@@ -111,7 +111,7 @@ cdef class GLDraw:
self.redraw_period = .2
# Info.
self.info = { "resizable" : True }
self.info = { "resizable" : True, "additive" : True }
if not ANGLE:
self.info["renderer"] = "gl"
@@ -206,19 +206,21 @@ cdef class GLDraw:
# Handle swap control.
vsync = int(os.environ.get("RENPY_GL_VSYNC", "1"))
# Switch the
IF not ANGLE:
if ANGLE:
opengl = 0
resizable = 0
elif renpy.android:
opengl = pygame.OPENGL
resizable = 0
else:
opengl = pygame.OPENGL
pygame.display.gl_set_attribute(pygame.GL_SWAP_CONTROL, vsync)
pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8)
ELSE:
opengl = 0
# EGL automatically handles vsync for us.
if renpy.config.gl_resize:
resizable = pygame.RESIZABLE
else:
resizable = 0
if renpy.config.gl_resize:
resizable = pygame.RESIZABLE
else:
resizable = 0
try:
if fullscreen:
@@ -382,12 +384,13 @@ cdef class GLDraw:
return False
if ANGLE:
gltexture.use_angle()
elif version.startswith("OpenGL ES"):
gltexture.use_gles()
elif renpy.android:
self.redraw_period = 1.0
self.always_opaque = True
gltexture.use_gles()
else:
gltexture.use_gl()
@@ -433,7 +436,7 @@ cdef class GLDraw:
# Pick a texture environment subsystem.
if ANGLE or (allow_shader and use_subsystem(
if ANGLE or renpy.android or (allow_shader and use_subsystem(
glenviron_shader,
"RENPY_GL_ENVIRON",
"shader",
@@ -646,17 +649,18 @@ cdef class GLDraw:
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT)
clip = (0, 0, self.virtual_size[0], self.virtual_size[1])
self.default_clip = (0, 0, self.virtual_size[0], self.virtual_size[1])
clip = self.default_clip
self.upscale_factor = 1.0 * self.physical_size[0] / self.virtual_size[0]
if renpy.audio.music.get_playing("movie") and renpy.display.video.fullscreen:
surf = renpy.display.video.render_movie(self.virtual_size[0], self.virtual_size[1])
if surf is not None:
self.draw_transformed(surf, clip, 0, 0, 1.0, reverse)
self.draw_transformed(surf, clip, 0, 0, 1.0, 1.0, reverse)
else:
self.draw_transformed(surftree, clip, 0, 0, 1.0, reverse)
self.draw_transformed(surftree, clip, 0, 0, 1.0, 1.0, reverse)
if flip:
@@ -733,6 +737,7 @@ cdef class GLDraw:
double xo,
double yo,
double alpha,
double over,
render.Matrix2D reverse):
cdef render.Render rend
@@ -751,6 +756,7 @@ cdef class GLDraw:
yo,
reverse,
alpha,
over,
self.environ,
False)
@@ -759,7 +765,7 @@ cdef class GLDraw:
if isinstance(what, pygame.Surface):
tex = self.load_texture(what)
self.draw_transformed(tex, clip, xo, yo, alpha, reverse)
self.draw_transformed(tex, clip, xo, yo, alpha, over, reverse)
return 0
raise Exception("Unknown drawing type. " + repr(what))
@@ -777,10 +783,10 @@ cdef class GLDraw:
# of dissolve on Ren'Py proper.
self.draw_transformed(rend.children[0][0],
clip, xo, yo, alpha, reverse)
clip, xo, yo, alpha, over, reverse)
self.draw_transformed(rend.children[1][0],
clip, xo, yo, alpha * what.operation_complete, reverse)
clip, xo, yo, alpha * what.operation_complete, over, reverse)
else:
@@ -793,6 +799,7 @@ cdef class GLDraw:
yo,
reverse,
alpha,
over,
rend.operation_complete,
self.environ)
@@ -810,6 +817,7 @@ cdef class GLDraw:
yo,
reverse,
alpha,
over,
rend.operation_complete,
rend.operation_parameter,
self.environ)
@@ -835,6 +843,7 @@ cdef class GLDraw:
yo,
reverse,
alpha,
over,
self.environ,
True)
@@ -847,7 +856,7 @@ cdef class GLDraw:
# Non-aligned clipping uses RTT.
if reverse.ydx != 0 or reverse.xdy != 0:
tex = what.render_to_texture(True)
self.draw_transformed(tex, clip, xo, yo, alpha, reverse)
self.draw_transformed(tex, clip, xo, yo, alpha, over, reverse)
return 0
minx, miny, maxx, maxy = clip
@@ -864,7 +873,8 @@ cdef class GLDraw:
clip = (minx, miny, maxx, maxy)
alpha = alpha * rend.alpha
over = over * rend.over
# If our alpha has hit 0, don't do anything.
if alpha <= 0.003: # (1 / 256)
return 0
@@ -878,7 +888,7 @@ cdef class GLDraw:
tcxo = reverse.xdx * cxo + reverse.xdy * cyo
tcyo = reverse.ydx * cxo + reverse.ydy * cyo
self.draw_transformed(child, clip, xo + tcxo, yo + tcyo, alpha, child_reverse)
self.draw_transformed(child, clip, xo + tcxo, yo + tcyo, alpha, over, child_reverse)
return 0
@@ -899,9 +909,10 @@ cdef class GLDraw:
glClear(GL_COLOR_BUFFER_BIT)
clip = (0, 0, what.width, what.height)
self.default_clip = (0, 0, what.width, what.height)
clip = self.default_clip
self.draw_transformed(what, clip, 0, 0, 1.0, reverse)
self.draw_transformed(what, clip, 0, 0, 1.0, 1.0, reverse)
if isinstance(what, render.Render):
what.is_opaque()
@@ -939,7 +950,7 @@ cdef class GLDraw:
clip = (0, 0, 1, 1)
self.draw_transformed(what, clip, 0, 0, 1.0, reverse)
self.draw_transformed(what, clip, 0, 0, 1.0, 1.0, reverse)
cdef unsigned char pixel[4]
@@ -978,7 +989,7 @@ cdef class GLDraw:
clip = (0, 0, width, height)
draw.draw_transformed(what, clip, 0, 0, 1.0, reverse)
draw.draw_transformed(what, clip, 0, 0, 1.0, 1.0, reverse)
if isinstance(what, render.Render):
what.is_opaque()
@@ -1063,6 +1074,7 @@ cdef class GLDraw:
y,
IDENTITY,
1.0,
1.0,
self.environ,
False)
+116 -7
View File
@@ -121,6 +121,27 @@ varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
vec4 color0 = texture2D(tex0, TexCoord0.st);
gl_FragColor = color0 * Color;
}
"""
BLIT_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
varying vec2 TexCoord0;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
@@ -149,6 +170,33 @@ varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
vec4 color0 = texture2D(tex0, TexCoord0.st);
vec4 color1 = texture2D(tex1, TexCoord1.st);
gl_FragColor = mix(color0, color1, done) * Color;
}
"""
BLEND_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform float done;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
@@ -182,6 +230,38 @@ varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
vec4 color0 = texture2D(tex0, TexCoord0.st);
vec4 color1 = texture2D(tex1, TexCoord1.st);
vec4 color2 = texture2D(tex2, TexCoord2.st);
float a = clamp((color0.a + offset) * multiplier, 0.0, 1.0);
gl_FragColor = mix(color1, color2, a) * Color;
}
"""
IMAGEBLEND_CLIP_SHADER = """
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 Color;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float offset;
uniform float multiplier;
varying vec2 TexCoord0;
varying vec2 TexCoord1;
varying vec2 TexCoord2;
varying vec2 pos;
uniform vec2 clip0;
uniform vec2 clip1;
void main()
{
if (pos.x < clip0.x || pos.y < clip0.y || pos.x >= clip1.x || pos.y >= clip1.y) {
@@ -200,6 +280,7 @@ void main()
def check_status(shader, handle, type):
"""
Checks the status of a shader or program. If it fails, then an
@@ -351,6 +432,11 @@ cdef class ShaderEnviron(Environ):
cdef Program blend_program
cdef Program imageblend_program
cdef Program blit_clip_program
cdef Program blend_clip_program
cdef Program imageblend_clip_program
cdef bint clipping
cdef double clip_x0
cdef double clip_y0
cdef double clip_x1
@@ -361,11 +447,17 @@ cdef class ShaderEnviron(Environ):
cdef int viewport_w
cdef int viewport_h
def init(self):
self.blit_program = Program(VERTEX_SHADER1, BLIT_SHADER)
self.blit_clip_program = Program(VERTEX_SHADER1, BLIT_CLIP_SHADER)
self.blend_program = Program(VERTEX_SHADER2, BLEND_SHADER)
self.blend_clip_program = Program(VERTEX_SHADER2, BLEND_CLIP_SHADER)
self.imageblend_program = Program(VERTEX_SHADER3, IMAGEBLEND_SHADER)
self.imageblend_clip_program = Program(VERTEX_SHADER3, IMAGEBLEND_CLIP_SHADER)
# The current program.
self.program = None
@@ -392,19 +484,27 @@ cdef class ShaderEnviron(Environ):
glUseProgramObjectARB(program.program)
glUniformMatrix4fvARB(program.Projection, 1, GL_FALSE, self.projection)
glUniform2fARB(program.clip0, self.clip_x0, self.clip_y0)
glUniform2fARB(program.clip1, self.clip_x1, self.clip_y1)
if self.clipping:
glUniform2fARB(program.clip0, self.clip_x0, self.clip_y0)
glUniform2fARB(program.clip1, self.clip_x1, self.clip_y1)
cdef void blit(self):
program = self.blit_program
if self.clipping:
program = self.blit_clip_program
else:
program = self.blit_program
if self.program is not program:
self.activate(program)
glUniform1iARB(program.tex0, 0)
cdef void blend(self, double fraction):
program = self.blend_program
if self.clipping:
program = self.blend_clip_program
else:
program = self.blend_program
if self.program is not program:
self.activate(program)
@@ -415,7 +515,10 @@ cdef class ShaderEnviron(Environ):
cdef void imageblend(self, double fraction, int ramp):
program = self.imageblend_program
if self.clipping:
program = self.imageblend_clip_program
else:
program = self.imageblend_program
if self.program is not program:
self.activate(program)
@@ -508,10 +611,15 @@ cdef class ShaderEnviron(Environ):
cdef int cx, cy, cw, ch
cdef int psw, psh
if clip_box == draw.default_clip:
self.unset_clip(draw)
return
minx, miny, maxx, maxy = clip_box
psw, psh = draw.physical_size
# The clipping box.
# The clipping box.
self.clipping = True
self.clip_x0 = minx
self.clip_y0 = miny
self.clip_x1 = maxx
@@ -566,7 +674,8 @@ cdef class ShaderEnviron(Environ):
cdef void unset_clip(self, GLDraw draw):
glDisable(GL_SCISSOR_TEST)
self.clipping = False
self.clip_x0 = 0
self.clip_y0 = 0
self.clip_x1 = 65535
+3
View File
@@ -42,6 +42,7 @@ cpdef blit(
double sy,
render.Matrix2D transform,
double alpha,
double over,
Environ environ,
bint nearest)
@@ -52,6 +53,7 @@ cpdef blend(
double sy,
render.Matrix2D transform,
double alpha,
double over,
double fraction,
Environ environ)
@@ -63,6 +65,7 @@ cpdef imageblend(
double sy,
render.Matrix2D transform,
double alpha,
double over,
double fraction,
int ramp,
Environ environ)
+16 -24
View File
@@ -52,7 +52,7 @@ cdef GLenum rtt_format = GL_RGBA
cdef GLenum rtt_internalformat = GL_RGBA
cdef GLenum rtt_type = GL_UNSIGNED_BYTE
def use_angle():
def use_gles():
global tex_format
global tex_internalformat
global tex_type
@@ -68,22 +68,6 @@ def use_angle():
rtt_internalformat = GL_RGBA
rtt_type = GL_UNSIGNED_BYTE
def use_gles():
global tex_format
global tex_internalformat
global tex_type
global rtt_format
global rtt_internalformat
global rtt_type
tex_format = GL_RGBA
tex_internalformat = GL_RGBA
tex_type = GL_UNSIGNED_BYTE
rtt_format = GL_RGB
rtt_internalformat = GL_RGB
rtt_type = GL_UNSIGNED_BYTE
def use_gl():
global tex_format
global tex_internalformat
@@ -889,7 +873,7 @@ def align_axes(*args):
return rv
cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, double alpha, Environ environ, bint nearest):
cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, double alpha, double over, Environ environ, bint nearest):
"""
This draws texgrid `tg` to the screen. `sx` and `sy` are offsets from
the upper-left corner of the screen.
@@ -898,6 +882,8 @@ cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, doub
texgrid coordinates to screen coordinates.
`alpha` is the alpha multiplier applied, from 0.0 to 1.0.
`over` is the over blending factor.
"""
cdef int x, y
@@ -906,7 +892,7 @@ cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, doub
tg.make_ready(nearest)
environ.blit()
environ.set_color(alpha, alpha, alpha, alpha)
environ.set_color(alpha, alpha, alpha, over * alpha)
y = 0
@@ -932,7 +918,7 @@ cpdef blit(TextureGrid tg, double sx, double sy, render.Matrix2D transform, doub
y += texh
cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matrix2D transform, double alpha, double fraction, Environ environ):
cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matrix2D transform, double alpha, double over, double fraction, Environ environ):
"""
Blends two textures to the screen.
@@ -945,6 +931,8 @@ cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matri
`alpha` is the alpha multiplier applied, from 0.0 to 1.0.
`over` is the over blending factor.
`fraction` is the fraction of the second texture to show.
"""
@@ -952,7 +940,7 @@ cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matri
tg1.make_ready(False)
environ.blend(fraction)
environ.set_color(alpha, alpha, alpha, alpha)
environ.set_color(alpha, alpha, alpha, over * alpha)
y = 0
@@ -985,8 +973,9 @@ cpdef blend(TextureGrid tg0, TextureGrid tg1, double sx, double sy, render.Matri
x += t0w
y += t0h
cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, double sy, render.Matrix2D transform, double alpha, double fraction, int ramp, Environ environ):
cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, double sy, render.Matrix2D transform, double alpha, double over, double fraction, int ramp, Environ environ):
"""
This uses texture 0 to control the blending of tetures 1 and 2 to
the screen.
@@ -998,7 +987,10 @@ cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, d
`transform` is the transform to apply to the texgrid, when going from
texgrid coordinates to screen coordinates.
`alpha` is the alpha multiplier applied, from 0.0 to 1.0.
`over` is the over blending factor.
`additive` is the additive blending factor, which is 1.0 for fully additive,
and 0.0 for fully over blending.
`fraction` is the fraction of the second texture to show.
@@ -1011,7 +1003,7 @@ cpdef imageblend(TextureGrid tg0, TextureGrid tg1, TextureGrid tg2, double sx, d
tg2.make_ready(False)
environ.imageblend(fraction, ramp)
environ.set_color(alpha, alpha, alpha, alpha)
environ.set_color(alpha, alpha, alpha, over * alpha)
y = 0
+21 -4
View File
@@ -26,12 +26,29 @@ from cStringIO import StringIO
import sys
import types
# Ensure the utf-8 codec is loaded, to prevent recursion when we use it
# to look up filenames.
u"".encode("utf-8")
try:
import android.apk
apks = [
android.apk.APK(prefix='assets/x-game/'),
android.apk.APK(prefix='assets/x-common/'),
]
expansion = os.environ.get("ANDROID_EXPANSION", None)
if expansion is not None:
print "Using expansion file", expansion
apks = [
android.apk.APK(apk=expansion, prefix='assets/x-game/'),
android.apk.APK(apk=expansion, prefix='assets/x-common/'),
]
else:
print "Not using expansion file."
apks = [
android.apk.APK(prefix='assets/x-game/'),
android.apk.APK(prefix='assets/x-common/'),
]
except ImportError:
apks = [ ]
+1 -1
View File
@@ -73,7 +73,7 @@ class LogFile(object):
return False
try:
base = os.environ.get("RENPY_LOG_BASE", renpy.config.basedir)
base = os.environ.get("RENPY_LOG_BASE", renpy.config.logdir)
fn = os.path.join(base, self.name + ".txt")
altfn = os.path.join(tempfile.gettempdir(), "renpy-" + self.name + ".txt")
+6 -4
View File
@@ -86,6 +86,7 @@ def run(restart):
renpy.store._restart = restart
# We run until we get an exception.
renpy.display.interface.enter_context()
renpy.execution.run_context(True)
@@ -153,6 +154,10 @@ def main():
renpy.config.commondir = commondir
else:
renpy.config.commondir = None
if renpy.android:
renpy.config.searchpath = [ ]
renpy.config.commondir = None
# Load Ren'Py extensions.
for dir in renpy.config.searchpath: #@ReservedAssignment
@@ -332,10 +337,7 @@ def main():
except game.QuitException, e:
if e.relaunch:
if renpy.windows and sys.argv[0].endswith(".exe"):
subprocess.Popen(sys.argv)
else:
subprocess.Popen([sys.executable, "-OO"] + sys.argv)
subprocess.Popen([sys.executable, "-EOO"] + sys.argv)
break
+7 -3
View File
@@ -1,3 +1,4 @@
# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
#
# Permission is hereby granted, free of charge, to any person
@@ -298,6 +299,10 @@ def list_logical_lines(filename, filedata=None):
word = prefix + rest
line += word
if len(line) > 65536:
raise ParseError(filename, start_number, "Overly long logical line. (Check strings and parenthesis.)", line=line, first=True)
pos = m.end(0)
# print repr(data[pos:])
@@ -938,9 +943,6 @@ class Lexer(object):
for _fn, ln, text, subblock in block:
if o.line > ln:
assert False
while o.line < ln:
rv.append(indent + '\n')
o.line += 1
@@ -1267,6 +1269,7 @@ def parse_parameters(l):
names.add(name)
if l.match(r'='):
l.skip_whitespace()
default = l.delimited_python("),")
else:
default = None
@@ -1324,6 +1327,7 @@ def parse_arguments(l):
l.revert(state)
name = None
l.skip_whitespace()
arguments.append((name, l.delimited_python("),")))
if l.match(r'\)'):
+43 -4
View File
@@ -479,13 +479,21 @@ def mutator(method):
if id(self) not in mutated:
mutated[id(self)] = ( weakref.ref(self), self.get_rollback())
mutate_flag = True
return method(self, *args, **kwargs)
return do_mutation
class RevertableList(list):
def __init__(self, *args):
log = renpy.game.log
if log is not None:
log.mutated[id(self)] = None
list.__init__(self, *args)
__delitem__ = mutator(list.__delitem__)
__delslice__ = mutator(list.__delslice__)
__setitem__ = mutator(list.__setitem__)
@@ -526,6 +534,14 @@ def revertable_sorted(*args, **kwargs):
class RevertableDict(dict):
def __init__(self, *args, **kwargs):
log = renpy.game.log
if log is not None:
log.mutated[id(self)] = None
dict.__init__(self, *args, **kwargs)
__delitem__ = mutator(dict.__delitem__)
__setitem__ = mutator(dict.__setitem__)
clear = mutator(dict.clear)
@@ -561,6 +577,14 @@ class RevertableDict(dict):
class RevertableSet(sets.Set):
def __init__(self, *args):
log = renpy.game.log
if log is not None:
log.mutated[id(self)] = None
sets.Set.__init__(self, *args)
__iand__ = mutator(sets.Set.__iand__)
__ior__ = mutator(sets.Set.__ior__)
__isub__ = mutator(sets.Set.__isub__)
@@ -610,6 +634,15 @@ class RevertableSet(sets.Set):
class RevertableObject(object):
def __new__(cls, *args, **kwargs):
self = super(RevertableObject, cls).__new__(cls, *args, **kwargs)
log = renpy.game.log
if log is not None:
log.mutated[id(self)] = None
return self
def __setattr__(self, attr, value):
object.__setattr__(self, attr, value)
@@ -783,7 +816,8 @@ class Rollback(renpy.object.Object):
"""
for obj, roll in reversed(self.objects):
obj.rollback(roll)
if roll is not None:
obj.rollback(roll)
for name, changes in self.stores.iteritems():
store = store_dicts.get(name, None)
@@ -925,7 +959,12 @@ class RollbackLog(renpy.object.Object):
self.current.objects = [ ]
try:
for _k, (ref, roll) in self.mutated.iteritems():
for _k, v in self.mutated.iteritems():
if v is None:
continue
(ref, roll) = v
obj = ref()
if obj is None:
@@ -1239,7 +1278,7 @@ class RollbackLog(renpy.object.Object):
store[name] = value
# Now, rollback to an acceptable point.
self.rollback(0, force=True, label=label)
self.rollback(0, force=True, label=label, greedy=False)
# Because of the rollback, we never make it this far.
+17 -3
View File
@@ -107,9 +107,23 @@ def expand_anchor(v):
"""
Turns an anchor into a number.
"""
return anchors.get(v, v)
try:
return anchors.get(v, v)
except:
# This fixes some bugs in very old Ren'Pys.
for n in anchors:
o = getattr(renpy.store, n, None)
if o is None:
continue
if v is o:
return anchors[n]
raise
# A map of properties that we know about. The properties may take a
# function that is called to convert the argument to something more
+18 -1
View File
@@ -145,6 +145,11 @@ class SFont(ImageFont):
self.advance[u' '] = self.spacewidth
self.offsets[u' '] = (0, 0)
self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, height), True)
self.width[u'\u200b'] = 0
self.advance[u'\u200b'] = 0
self.offsets[u'\u200b'] = (0, 0)
self.chars[u'\u00a0'] = self.chars[u' ']
self.width[u'\u00a0'] = self.width[u' ']
self.advance[u'\u00a0'] = self.advance[u' ']
@@ -252,13 +257,19 @@ class MudgeFont(ImageFont):
self.advance[u' '] = self.spacewidth
self.offsets[u' '] = (0, 0)
if u'\u00a0' not in self.chars:
self.chars[u'\u00a0'] = self.chars[u' ']
self.width[u'\u00a0'] = self.width[u' ']
self.advance[u'\u00a0'] = self.advance[u' ']
self.offsets[u'\u00a0'] = self.offsets[u' ']
self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, height), True)
self.width[u'\u200b'] = 0
self.advance[u'\u200b'] = 0
self.offsets[u'\u200b'] = (0, 0)
def parse_bmfont_line(l):
w = ""
line = [ ]
@@ -342,6 +353,12 @@ class BMFont(ImageFont):
self.advance[u'\u00a0'] = self.advance[u' ']
self.offsets[u'\u00a0'] = self.offsets[u' ']
self.chars[u'\u200b'] = renpy.display.pgrender.surface((0, self.height), True)
self.width[u'\u200b'] = 0
self.advance[u'\u200b'] = 0
self.offsets[u'\u200b'] = (0, 0)
def register_sfont(name=None, size=None, bold=False, italics=False, underline=False,
filename=None, spacewidth=10, default_kern=0, kerns={},
+4 -1
View File
@@ -314,7 +314,7 @@ cdef class FTFont:
rv.index = index
error = FT_Load_Glyph(face, index, 0)
error = FT_Load_Glyph(face, index, FT_LOAD_FORCE_AUTOHINT)
if error:
raise FreetypeError(error)
@@ -525,6 +525,9 @@ cdef class FTFont:
if glyph.split == SPLIT_INSTEAD:
continue
if glyph.character == 0x200b:
continue
x = glyph.x + xo
y = glyph.y + yo
+11 -3
View File
@@ -402,7 +402,7 @@ class Layout(object):
`width`, `height`
The height of the laid-out text.
"""
style = text.style
self.line_overlap_split = style.line_overlap_split
@@ -1020,6 +1020,10 @@ class Layout(object):
return 0
# The maximum number of entries in the layout cache.
LAYOUT_CACHE_SIZE = 50
# Maps from a text to the layout of that text - in an old and new generation.
layout_cache_old = { }
layout_cache_new = { }
@@ -1087,9 +1091,9 @@ class Text(renpy.display.core.Displayable):
self.start = None
self.end = None
self.dirty = True
def __init__(self, text, slow=None, scope=None, substitute=None, slow_done=None, replaces=None, **properties):
super(Text, self).__init__(**properties)
# We need text to be a list, so if it's not, wrap it.
@@ -1357,6 +1361,10 @@ class Text(renpy.display.core.Displayable):
if layout is None or layout.width != width or layout.height != height:
layout = Layout(self, width, height, renders)
if len(layout_cache_new) > LAYOUT_CACHE_SIZE:
layout_cache_new.clear()
layout_cache_new[id(self)] = layout
# The laid-out size of this Text.
+1 -1
View File
@@ -315,7 +315,7 @@ class StringTranslator(object):
new = self.translations.get(notags, None)
if new is not None:
return True
return new
return old
+1 -1
View File
@@ -25,5 +25,5 @@ fi
if [ "$1" = "--build" ] ; then
echo "Ren'Py build complete."
else
exec ./renpy.py "$@"
exec python -OO ./renpy.py "$@"
fi
+26
View File
@@ -447,3 +447,29 @@ And install the packages. Then, try building your game again.
http://lemmasoft.renai.us/forums/viewtopic.php?f=32&t=13987&hilit=rapt
Expansion APKs
--------------
|PGS4A| optionally supports the use of expansion APKs when used on a
device supporting Google Play. Please see:
http://developer.android.com/google/play/expansion-files.html
For information about expansion APKs work. Right now, only the
main expansion APK is supported, giving a 2GB limit.
.. ifconfig:: not is_renpy
When an android APK is created, all assets (from the assets
directory) are placed in the expansion APK. The assets can be
accessed through the :module:`android.assets` interface.
.. ifconfig:: is_renpy
When an APK is created, all game files will be placed in the
expansion APK. Ren'Py will transparently use these files.
|PGS4A| will place the expansion APK on the device when installing
the APK package on the device. In normal operation, Google Play will
place the expansion APK on the device automatically.
-12
View File
@@ -45,20 +45,8 @@ due to the Android software and hardware are:
screen. When the user is not touching the screen, the virtual
pointer will move to the upper-left corner of the screen.
* :func:`ImageDissolve`, :func:`AlphaDissolve`, and :func:`AlphaBlend`
are not supported.
* Functions that render to a texture can only render an opaque
texture. This means that the :func:`Dissolve` and :func:`Pixellate`
transitions will only produce opaque output.
* The :propref:`focus_mask` property is not supported. It will be
treated as if all pixels in the mask are opaque.
* Movie playback is not supported.
* Launching the web browser is not supported.
* Some python modules (including network communication) modules are
not supported.
+31
View File
@@ -751,6 +751,37 @@ both horizontal and vertical positions.
This controls the opacity of the displayable.
The alpha transform is applied to each image comprising the child of
the transform independently. This can lead to unexpected results when
the children overlap, such as as seeing a character through clothing.
The :func:`Flatten` displayable can help with these problems.
.. transform-property:: additive
:type: float
:default: 0.0
This controls how much additive blending Ren'Py performs. When 1.0,
Ren'Py draws using the ADD operator. When 0.0, Ren'Py draws using
the OVER operator.
Additive blending is performed on each child of the transform independently.
Fully additive blending doesn't alter the alpha channel of the destination,
and additive images may not be visible if they're not drawn directly onto
an opaque surface. (Complex operations, like viewport, :func:`Flatten`, :func:`Frame`,
and certain transitions may cause problems with additive blending.)
.. warning::
Additive blending is only supported by hardware-based renderers, such
as the OpenGL and DirectX/ANGLE renderers. The software renderer will
draw additive images incorrectly.
Once the graphics system has started, ``renpy.get_renderer_info()["additive"]``
will be true if additive blending is supported.
.. transform-property:: around
:type: (position, position)
+11
View File
@@ -209,8 +209,19 @@ Please think twice about archiving your game. Keeping files open will
help others run your game on future platforms - platforms that may not
exist until after you're gone.
Build Functions
---------------
.. include:: inc/build
Advanced Configuration
----------------------
The following variables provide further control of the build process:
.. var:: build.exclude_empty_directories = True
If true, empty directories (including directories left empty by
file archiving) will be removed from generated packages. If false,
empty directories will be included.
+129 -51
View File
@@ -2,6 +2,85 @@
Full Changelog
==============
Ren'Py 6.15.6
-------------
This release includes improvements for the Android platform:
* Assets are now read exclusively from the APK and expansion file.
* Logs and tracebacks are placed on external storage.
* Saves are placed on external storage, except when saves from
older versions of Ren'Py exist.
The GL2 shaders Ren'Py uses have been simplified in the (usual) case
where no clipping is occuring. This leads to a noticable speed
improvement on Android, and potentially other platforms as well.
An issue with Drag-and-drop has been fixed. Thanks go to Kinsman
for contributing this fixe.
The :func:`Skip` action now triggers the skip indicator. It also
supports a new fast parameter, which causes skipping to the
next menu.
This release includes various minor changes to improve compatibility
with very old Ren'Py games. (It now runs the Ren'Py 5 demo.)
Ren'Py 6.15.5
-------------
This release adds two new features:
* The GL renderer now supports additive blending. This is enabled using the
:tpref:`additive` transform property in an ATL transform or use of the
:func:`Transform` class. Additive blending will not work if the software
renderer is in use, and it's up to creators to deal with that issue.
* The new :func:`Flatten` displayable combines multiple textures into
a single texture. This can be used to prevent incorrect behavior
when a displayable containing multiple overlapping textures (like a
:func:`LiveComposite` is shown with an :tpref:`alpha` between 0 and 1.
It also fixes the following issues:
* Whitespace is now skipped before default arguments, which previously
caused parse errors in some cases.
* Ren'Py now sets the unix mode of files and directories in zip and tar
files to 644 and 755 as appropriate. Prior versions of Ren'Py used
666 and 777 as the permissions, which lead to a security problem
when the file was unpacked by a tool that didn't respect the user's
umask. (Info-zip had this problem.)
* Auto-hinting for fonts is now enabled by default. This restores font
rendering compatibility with prior releases.
* Ren'Py now builds with and requires the current version of libav. It
should also work with current versions of ffmpeg when libav is
available.
* The version of SDL distributed with Ren'Py has been patched to
prevent multiple windows from showing up in the Window menu
when entering and leaving fullscreen mode.
Ren'Py 6.15.4
-------------
This release fixes a compile problem that prevented Ren'Py 6.14.x and Ren'Py
6.15.0-3 from running on most 64-bit Linux systems.
Image prediction has become more fine-grained, and can take place while the
screen is animating.
The new :var:`build.exclude_empty_directories` determines if empty directories
are include or excluded from the distribution. It defaults to true,
previously the default was platform-dependant.
Ren'Py 6.15
===========
@@ -18,21 +97,20 @@ combined at the translator's discretion. As most Ren'Py statements are
allowed inside the new translation blocks, it's possible to use logic (like
conditions) to tailor the translations to your language.
The launcher includes a new "Generate Translations" button, which - as part
of a sanctioned translation where the full script is present - will generate
The launcher includes a new "Generate Translations" button, which - as part of
a sanctioned translation where the full script is present - will generate
empty translation files for a new language.
Improved Japanese Support
-------------------------
Ren'Py 6.15 includes multiple changes to better support the Japanese
language.
Ren'Py 6.15 includes multiple changes to better support the Japanese language.
* The tutorial game has been translated to Japanese, with the language
being selectable from the preferences menu.
* The tutorial game has been translated to Japanese, with the language being
selectable from the preferences menu.
The tutorial was translated by Koichi Akabe.
* Support for vertical writing has been added to Ren'Py. Consisting of the
:propref:`vertical` style property for text, and the new
:propref:`box_reverse` property on hboxes, this support makes it possible
@@ -96,14 +174,13 @@ replay if one is in progress, and is ignored otherwise.
Voice Improvements
------------------
There have been several improvements to the voice playback system. The
new :var:`config.voice_filename_format` variable makes it possible to
use only part of the filename in a voice statement. The new voice_tag
parameter to :func:`Character`, in conjunction with the
:func:`SetVoiceMute` and :func:`ToggleVoiceMute` actions, makes it
possible to selectively mute particular characters' voices. The new
:func:`VoiceReplay` action makes it possible to replay the current
voice.
There have been several improvements to the voice playback system. The new
:var:`config.voice_filename_format` variable makes it possible to use only
part of the filename in a voice statement. The new voice_tag parameter to
:func:`Character`, in conjunction with the :func:`SetVoiceMute` and
:func:`ToggleVoiceMute` actions, makes it possible to selectively mute
particular characters' voices. The new :func:`VoiceReplay` action makes it
possible to replay the current voice.
Launcher Improvements
---------------------
@@ -124,44 +201,44 @@ There were a few launcher improvements in this release.
Macintosh Changes
-----------------
The Macintosh version of Ren'Py now requires a 64-bit capable processor,
and Mac OS X 10.6 or newer.
The Macintosh version of Ren'Py now requires a 64-bit capable processor, and
Mac OS X 10.6 or newer.
Packaging Improvements
----------------------
The file layout of Ren'Py games has been somewhat altered. With the
exception of small launcher programs, all platform-dependent binaries
are under the lib/ directory. Ren'Py itself has now been placed in the
renpy/ directory. The common/ directory has been moved to
renpy/common/, as it's considered an integral part of Ren'Py.
The file layout of Ren'Py games has been somewhat altered. With the exception
of small launcher programs, all platform-dependent binaries are under the
lib/ directory. Ren'Py itself has now been placed in the renpy/ directory.
The common/ directory has been moved to renpy/common/, as it's considered an
integral part of Ren'Py.
Ren'Py now uses renamed but otherwise unmodified python binaries on
all desktop platforms. (Previously, it used platform-specific
binaries.) Portions of the library are shared between the desktop
builds.
Ren'Py now uses renamed but otherwise unmodified python binaries on all
desktop platforms. (Previously, it used platform-specific binaries.) Portions
of the library are shared between the desktop builds.
A running Ren'Py process on Linux will now be named after the game, rather
than having python as a name.
A running Ren'Py process on Linux will now be named after the game,
rather than having python as a name.
Other Changes
-------------
* :ref:`Viewports <sl-viewport>` now support edge scrolling, which
scrolls the viewport when the mouse is within a a configurable
distance of the viewport edge.
* :ref:`Viewports <sl-viewport>` now support edge scrolling, which scrolls
the viewport when the mouse is within a a configurable distance of the
viewport edge.
* Most keyboard keys now automatically repeat. The repeat rate is
controlled by :var:`config.key_repeat`.
* Most keyboard keys now automatically repeat. The repeat rate is controlled
by :var:`config.key_repeat`.
* Side images can now be used with menus.
* The :ref:`config.enter_yesno_transition` and `config.exit_yesno_transition`
variables make it possible to define a transition that is run when
yes/no prompts appear and disappear, respectively.
* The :ref:`config.enter_yesno_transition` and
`config.exit_yesno_transition` variables make it possible to define a
transition that is run when yes/no prompts appear and disappear,
respectively.
* The :ref:`viewport statement <sl-viewport>` now supports edge scrolling -
automatic scrolling when the mouse approaches the sides of the viewport.
@@ -216,20 +293,21 @@ Among others, the following bugs were fixed:
* :ghbug:`51`: The slow_done callback was not called after a rollback.
* :ghbug:`56`, :ghbug:`57`: :func:`renpy.loadable` now works with Android assets.
* :ghbug:`56`, :ghbug:`57`: :func:`renpy.loadable` now works with Android
assets.
* :ghbug:`60`: Fixed a bug that prevented {p} and {w} from working properly when followed
immediately by a text tag.
* :ghbug:`61`: Ren'Py no longer crashes when an end_game_transition is
set and a screen uses a variable that is no longer defined when the
game restarts.
* :ghbug:`60`: Fixed a bug that prevented {p} and {w} from working properly
when followed immediately by a text tag.
* :ghbug:`65`: Multiplying a rollback list by a number now always produces
a rollback list.
* :ghbug:`61`: Ren'Py no longer crashes when an end_game_transition is set
and a screen uses a variable that is no longer defined when the game
restarts.
* :ghbug:`65`: Multiplying a rollback list by a number now always produces a
rollback list.
* Editra should work better on Windows.
* It's now possible to :func:`renpy.call` a label that doesn't take
parameters.
@@ -239,7 +317,7 @@ Among others, the following bugs were fixed:
* Fixed an error handling failure when a python early block contained a
syntax error.
Ren'Py 6.14
===========
@@ -463,7 +541,7 @@ Other Changes
* The :func:`renpy.call` function allows - with major and important caveats
- a call to a Ren'Py label to begin from inside python code. Such a call
immediately terminates the current statement.
immediately terminates the current statement.
* When an action is expected, nested lists of actions can be given. The
lists are flattened and the action executed.
+12
View File
@@ -934,6 +934,18 @@ Rarely or Internally Used
wav files are of a lower rate, changing this to that rate may make
things more efficent.
.. var:: config.start_callbacks = [ ... ]
A list of callbacks functions that are called with no arguments
after the init phase, but before the game (including the
splashscreen) starts. This is intended to be used by frameworks
to initialize variables that will be saved.
The default value of this variable includes callbacks that Ren'Py
uses internally to implement features such as nvl-mode. New
callbacks can be appended to this list, but the existing callbacks
should not be removed.
.. var:: config.start_interact_callbacks = ...
A list of functions that are called (without any arguments) when
+1 -1
View File
@@ -1,5 +1,5 @@
Dealing With Display Problems
=============================
-----------------------------
As of version 6.13, Ren'Py will take advantage of graphics
acceleration hardware, if it's present and functional. Using hardware
+13
View File
@@ -1,5 +1,18 @@
.. Automatically generated file - do not modify.
.. function:: Flatten(child, **properties)
This flattens `child`, which may be made up of multiple textures, into
a single texture.
Certain operations, like the alpha transform property, apply to every
texture making up a displayable, which can yield incorrect results
when the textures overlap on screen. Flatten creates a single texture
from multiple textures, which can prevent this problem.
Flatten is a relatively expensive operation, and so should only be used
when absolutely required.
.. function:: Frame(image, xborder, yborder, tile=False, **properties)
A displayable that resizes an image to fill the available area,
+12
View File
@@ -0,0 +1,12 @@
.. Automatically generated file - do not modify.
.. function:: renpy.showing(name, layer='master')
Returns true if an image with the same tag as `name` is showing on
`layer`
`image`
May be a string giving the image name or a tuple giving each
component of the image name. It may also be a string giving
only the image tag.
+25
View File
@@ -0,0 +1,25 @@
.. Automatically generated file - do not modify.
.. function:: renpy.input(prompt, default='', allow=None, exclude='{}', length=None, with_none=None)
Calling this function pops up a window asking the player to enter some
text. It returns the entered text.
`prompt`
A string giving a prompt to display to the player.
`default`
A string giving the initial text that will be edited by the player.
`allow`
If not None, a string giving a list of characters that will
be allowed in the text.
`exclude`
If not None, if a character is present in this string, it is not
allowed in the text.
`length`
If not None, this must be an integer giving the maximum length
of the input string.
+5 -1
View File
@@ -1,6 +1,6 @@
.. Automatically generated file - do not modify.
.. class:: MusicRoom(channel='music', fadeout=0.0, fadein=0.0)
.. class:: MusicRoom(channel='music', fadeout=0.0, fadein=0.0, loop=False)
A music room that contains a series of songs that can be unlocked
by the user, and actions that can play entries from the list in
@@ -16,6 +16,10 @@
`fadein`
The number of seconds it takes to fade in the new
music when changing tracks.
`loop`
If true, a music track will loop once played. If False, it
will advance to the next track.
.. method:: Next(self)
+6 -3
View File
@@ -63,9 +63,12 @@
One of ``"gl"`` or ``"sw"``, corresponding to the OpenGL and
software renderers, respectively.
``"resizable``
``"resizable"``
True if and only if the window is resizable.
``"additive"``
True if and only if the renderer supports additive blending.
Other, renderer-specific, keys may also exist. The dictionary should
be treated as immutable. This should only be called once the display
has been started (that is, after the init code is finished).
@@ -146,8 +149,8 @@
.. function:: renpy.set_physical_size(size)
Attempts to set the size of the physical window to size. This has the
side effect of taking the screen out of windowed mode.
Attempts to set the size of the physical window to `size`. This has the
side effect of taking the screen out of fullscreen mode.
.. function:: renpy.vibrate(duration)
+4 -1
View File
@@ -55,11 +55,14 @@
textbutton "Marsopolis":
action [ Jump("mars"), SelectedIf(mars_flag) ]
.. function:: Skip()
.. function:: Skip(fast=False)
Causes the game to begin skipping. If the game is in a menu
context, then this returns to the game. Otherwise, it just
enables skipping.
`fast`
If True, skips directly to the next menu choice.
.. function:: With(transition)
+12
View File
@@ -0,0 +1,12 @@
.. Automatically generated file - do not modify.
.. function:: build._(s)
Flags a string as translatable, and returns it immediately. The string
will be translated when text displays it.
.. function:: updater._(s)
Flags a string as translatable, and returns it immediately. The string
will be translated when text displays it.
+21
View File
@@ -65,6 +65,27 @@
`range`
The range of the value.
.. function:: VariableValue(variable, range, max_is_zero=False, style='bar', offset=0, step=None)
`variable`
A string giving the name of the variable to adjust.
`range`
The range to adjust over.
`max_is_zero`
If True, then when the field is zero, the value of the
bar will be range, and all other values will be shifted
down by 1. This works both ways - when the bar is set to
the maximum, the field is set to 0.
This is used internally, for some preferences.
`style`
The styles of the bar created.
`offset`
An offset to add to the value.
`step`
The amount to change the bar by. If None, defaults to 1/10th of
the bar.
.. function:: XScrollValue(viewport)
The value of an adjustment that horizontally scrolls the the viewport with the
+10 -1
View File
@@ -44,6 +44,15 @@ Text, Displayables, Transforms, and Transitions
transforms
transitions
atl
Other Functions
---------------
.. toctree::
:maxdepth: 1
input
Customizing Ren'Py
------------------
@@ -99,7 +108,7 @@ End-User Documentation
.. toctree::
:maxdepth: 1
display_problems
problems
Engine Developer Documentation
+35
View File
@@ -0,0 +1,35 @@
==========
Text Input
==========
With some limitations, Ren'Py can prompt the user to input a small
amount of text. This prompting is done by the renpy.input function,
which returns the entered text, allowing it to be saved in a variable
or otherwise processed.
Right now, text input is limited to languages that do not require
input method (IME) support. Most western languages should work, but
Chinese, Japanese, and Korean probably won't.
The renpy.input function is defined as:
.. include:: inc/input
Code that uses renpy.input will often want to process the result
further, using standard python string manipulation functions. For
example, the following code will ask the player for his or her
name and remove leading or trailing whitespace. If the name is
empty, it will be replaced by a default name. Finally, it is
displayed to the user. ::
define pov = Character("[povname]")
python:
povname = renpy.input("What is your name?")
povname = povname.strip()
if not povname:
povname = "Pat Smith"
pov "My name is [povname]!"
File diff suppressed because one or more lines are too long
+6 -5
View File
@@ -143,9 +143,6 @@ Ren'Py statements are made of a few basic parts.
letters.
:dfn:`Image Name`
An image name consists of one or more names, separated by
spaces. The name ends at the end of the statement, or when a
keyword is encountered.
An :dfn:`image name` consists of one or more names, separated by
spaces. The first component of the image name is called the
@@ -216,6 +213,8 @@ If the statement takes a block, the line ends with a colon
(:). Otherwise, the line just ends.
.. _python-basics:
Python Expression Syntax
========================
@@ -347,9 +346,11 @@ that the keyword arguments are described in the documentation.
Python is a lot more powerful than we have space for in this manual.
To learn Python in more detail, we recommend starting with the Python
tutorial, which is available from
`python.org <http://docs.python.org/release/2.6.6/tutorial/index.html>`_.
`python.org <http://docs.python.org/release/2.7/tutorial/index.html>`_.
While we don't think a deep knowledge of Python is necessary to work
with Ren'Py, learning about python expressions is helpful.
with Ren'Py, the basics of python statements and expressions is
often helpful.
+18
View File
@@ -0,0 +1,18 @@
Dealing with Problems
=====================
.. include:: display_problems.rst
64-Bit Linux Problems
----------------------
Ren'Py 6.14.x and 6.15.0-3 were compiled incorrectly, and will often
fail to operate on 64-bit Linux computers. The best way to work around
this is to download Ren'Py 6.15.4 or later, and use it to run the game::
/path/to/renpy-6.15.4/renpy.sh /path/to/game-with-problems
+97
View File
@@ -0,0 +1,97 @@
=================
Python Statements
=================
Ren'Py is written in the Python programmming language, and includes
support for including python code inside Ren'Py scripts. Python
support can be used for many things, from setting a flag to creating
new displayables. This chapter covers ways in which Ren'Py scripts can
directly invoke Ren'Py code, through the various python statements.
.. _python-statement:
Python
------
The python statement takes a block of python code, and runs that code
when control reaches the statement. A basic python statement can be
very simple::
python:
flag = True
Python statements can get more complex, when necessary::
python:
player_health = max(player_health - damage, 0)
if enemy_vampire:
enemy_health = min(enemy_health + damage, enemy_max_health)
There are two modifiers to the python statement that change its
behavior:
``hide``
If given the hide modifier, the python statement will run the
code in an anonymous scope. The scope will be lost when the python
block terminates.
This allows python code to use temporary variables that can't be
saved - but it means that the store needs to be accessed as fields
on the store object, rather than directly.
``in``
The ``in`` modifier takes a name. Instead of executing in the
default store, the python code will execute in the store that
name.
One-line Python Statement
-------------------------
A common case is to have a single line of python that runs in the
default store. For example, a python one-liner can be used to
initialize or update a flag. To make writing python one-liners
more convenient, there is the one-line python statement.
The one-line python statement begins with the dollar-sign ($)
character, and contains all of the code on that line. Here
are some example of python one-liners::
# Set a flag.
$ flag = True
# Increment a variable.
$ rabu_rabu_points += 1
# Call a function that exposes Ren'Py functionality.
$ renpy.movie_cutscene("opening.ogv")
Python one-liners always run in the default store.
Named Stores
------------
Named stores provide a way of organizing python code into modules. By
placing code in modules, you can minimize the chance of name
conflicts.
Named stores can be accessed by supplying the ``in`` clause to
``python`` or ``init python``, code can run accessed in a named
store. Each store corresponds to a python module. The default store is
``store``, while a named store is accessed a ``store``.`name`. These
python modules can be imported using the python import statement,
while names in the modules can be imported using the python from
statement.
Named stores participate in save, load, and rollback in the same way
that the default store does.
TODO:
* Note that names beginning with a single _ are reserved for Ren'Py's
use.

Some files were not shown because too many files have changed in this diff Show More