tdl

Package tdl |
[frames] | no frames]

# Source Code for Package tdl

       1  """
       2      The documentation for python-tdl.  A Pythonic port of
       3      U{libtcod<http://doryen.eptalys.net/libtcod/>}.
       4
       5      You can find the project page on Google Code
       6      U{here<http://code.google.com/p/python-tdl/>}.
       7
       8      Report any bugs or issues to the Google Code issue tracker
       9      U{here<https://code.google.com/p/python-tdl/issues/list>}.
      10
      11      Getting Started
      12      ===============
      13        Once the library is imported you can load the font you want to use with
      14        L{tdl.setFont}.
      15        This is optional and when skipped will use a decent default font.
      16
      17        After that you call L{tdl.init} to set the size of the window and get the
      18        root console in return.
      19        This console is the canvas to what will appear on the screen.
      20
      21      Indexing Consoles
      22      =================
      23        For most methods taking a position you can use Python-style negative
      24        indexes to refer to the opposite side of a console with (-1, -1)
      25        starting at the bottom right.
      26        You can also check if a point is part of a console using containment
      27        logic i.e. ((x, y) in console).
      28
      29        You may also iterate over a console using a for statement.  This returns
      30        every x,y coordinate available to draw on but it will be extremely slow
      31        to actually operate on every coordinate individualy.
      32        Try to minimize draws by using an offscreen L{Console}, only drawing
      33        what needs to be updated, and using L{Console.blit}.
      34
      35      Drawing
      36      =======
      37        Once you have the root console from L{tdl.init} you can start drawing on
      38        it using a method such as L{Console.drawChar}.
      39        When using this method you can have the char parameter be an integer or a
      40        single character string.
      41
      42        The fgcolor and bgcolor parameters expect a three item list
      43        [red, green, blue] with integers in the 0-255 range with [0, 0, 0] being
      44        black and [255, 255, 255] being white.
      45        Or instead you can use None in the place of any of the three parameters
      46        to tell the library to not overwrite colors.
      47        After the drawing functions are called a call to L{tdl.flush} will update
      48        the screen.
      49  """
      50
      51  import sys
      52  import os
      53
      54  import ctypes
      55  import weakref
      56  import array
      57  import itertools
      58  import textwrap
      59  import struct
      60  import re
      61  import warnings
      62
      63  from . import event, map, noise
      64  from .__tcod import _lib, _Color, _unpackfile
      65
      66  _IS_PYTHON3 = (sys.version_info[0] == 3)
      67
      68  if _IS_PYTHON3: # some type lists to use with isinstance
      69      _INTTYPES = (int,)
      70      _NUMTYPES = (int, float)
      71      _STRTYPES = (str, bytes)
      72  else:
      73      _INTTYPES = (int, long)
      74      _NUMTYPES = (int, long, float)
      75      _STRTYPES = (str,)
      76
      77 -def _encodeString(string): # still used for filepaths, and that's about it
      78      "changes string into bytes if running in python 3, for sending to ctypes"
      79      if _IS_PYTHON3 and isinstance(string, str):
      80          return string.encode()
      81      return string
      82
      83  #def _formatString(string):
      84  #    pass
      85
      86 -def _formatChar(char):
      87      """Prepares a single character for passing to ctypes calls, needs to return
      88      an integer but can also pass None which will keep the current character
      89      instead of overwriting it.
      90
      91      This is called often and needs to be optimized whenever possible.
      92      """
      93      if char is None:
      94          return None
      95      #if isinstance(char, _INTTYPES):
      96      #    return char
      97      if isinstance(char, _STRTYPES) and len(char) == 1:
      98          return ord(char)
      99      return int(char) # conversion faster than type check
     100      #raise TypeError('Expected char parameter to be a single character string, number, or None, got: %s' % repr(char))
     101
     102  _fontinitialized = False
     103  _rootinitialized = False
     104  _rootConsoleRef = None
     105  # remove dots from common functions
     106  _setchar = _lib.TCOD_console_set_char
     107  _setfore = _lib.TCOD_console_set_char_foreground
     108  _setback = _lib.TCOD_console_set_char_background
     109  _setcharEX = _lib.TCOD_console_put_char_ex
     110 -def _verify_colors(*colors):
     111      """Used internally.
     112      Raise an assertion error if the parameters can not be converted into colors.
     113      """
     114      for color in colors:
     115          assert _iscolor(color), 'a color must be a 3 item tuple, web format, or None, received %s' % repr(color)
     116      return True
     117
     118 -def _iscolor(color):
     119      """Used internally.
     120      A debug function to see if an object can be used as a TCOD color struct.
     121      None counts as a parameter to keep the current colors instead.
     122
     123      This function is often part of an inner-loop and can slow a program down.
     124      It has been made to work with assert and can be skipped with the -O flag.
     125      Still it's called often and must be optimized.
     126      """
     127      if color is None:
     128          return True
     129      if isinstance(color, (tuple, list, _Color)):
     130          return len(color) == 3
     131      if isinstance(color, _INTTYPES):
     132          return True
     133      return False
     134
     135  ## not using this for now
     136  #class Color(object):
     137  #
     138  #    def __init__(self, r, g, b):
     139  #        self._color = (r, g, b)
     140  #        self._ctype = None
     141  #
     142  #    def _getCType(self):
     143  #        if not self._ctype:
     144  #            self._ctype = _Color(*self._color)
     145  #        return self._ctype
     146  #
     147  #    def __len__(self):
     148  #        return 3
     149
     150  # Format the color to ctypes, will preserve None and False
     151  _formatColor = _Color.new
     152
     153 -def _getImageSize(filename):
     154      """Try to get the width and height of a bmp of png image file"""
     155      file = open(filename, 'rb')
     156      if file.read(8) == b'\x89PNG\r\n\x1a\n': # PNG
     157          while 1:
     158              length, = struct.unpack('>i', file.read(4))
     159              chunkID = file.read(4)
     160              if chunkID == '': # EOF
     161                  return None
     162              if chunkID == b'IHDR':
     163                  # return width, height
     164                  return struct.unpack('>ii', file.read(8))
     165              file.seek(4 + length, 1)
     166      file.seek(0)
     167      if file.read(8) == b'BM': # Bitmap
     168          file.seek(18, 0) # skip to size data
     169          # return width, height
     170          return struct.unpack('<ii', file.read(8))
     171      # return None on error, unknown file
     172
     173 -class TDLError(Exception):
     174      """
     175      The catch all for most TDL specific errors.
     176      """
     177
     178 -class _MetaConsole(object):
     179      """
     180      Contains methods shared by both the L{Console} and L{Window} classes.
     181      """
     182      __slots__ = ('width', 'height', 'console', '_cursor', '_fgcolor',
     183                   '_bgcolor', '_bgblend', '_colorLock', '__weakref__', '__dict__')
     184
     185 -    def __init__(self):
     186          self._cursor = (0, 0)
     187          self._scrollMode = 'error'
     188          self._fgcolor = _formatColor((255, 255, 255))
     189          self._bgcolor = _formatColor((0, 0, 0))
     190          self._bgblend = 1 # SET
     191          self._colorLock = None # which object sets the ctype color options
     192
     193 -    def _normalizePoint(self, x, y):
     194          """Check if a point is in bounds and make minor adjustments.
     195
     196          Respects Pythons negative indexes.  -1 starts at the bottom right.
     197          Replaces the _drawable function
     198          """
     199          #assert isinstance(x, _INTTYPES), 'x must be an integer, got %s' % repr(x)
     200          #assert isinstance(y, _INTTYPES), 'y must be an integer, got %s' % repr(y)
     201          # force int, always faster than type checking
     202          x = int(x)
     203          y = int(y)
     204
     205          assert (-self.width <= x < self.width) and (-self.height <= y < self.height), \
     206                  ('(%i, %i) is an invalid postition on %s' % (x, y, self))
     207
     208          # handle negative indexes
     209          if x < 0:
     210              x += self.width
     211          if y < 0:
     212              y += self.height
     213          return (x, y)
     214
     215 -    def _normalizeRect(self, x, y, width, height):
     216          """Check if the rectangle is in bounds and make minor adjustments.
     217          raise AssertionError's for any problems
     218          """
     219          x, y = self._normalizePoint(x, y) # inherit _normalizePoint logic
     220
     221          assert width is None or isinstance(width, _INTTYPES), 'width must be an integer or None, got %s' % repr(width)
     222          assert height is None or isinstance(height, _INTTYPES), 'height must be an integer or None, got %s' % repr(height)
     223
     224          # if width or height are None then extend them to the edge
     225          if width is None:
     226              width = self.width - x
     227          elif width < 0: # handle negative numbers
     228              width += self.width
     229              width = max(0, width) # a 'too big' negative is clamped zero
     230          if height is None:
     231              height = self.height - y
     232              height = max(0, height)
     233          elif height < 0:
     234              height += self.height
     235
     236          # reduce rect size to bounds
     237          width = min(width, self.width - x)
     238          height = min(height, self.height - y)
     239
     240          return x, y, width, height
     241
     242 -    def _normalizeCursor(self, x, y):
     243          """return the normalized the cursor position."""
     244          width, height = self.getSize()
     245          assert width != 0 and height != 0, 'can not print on a console with a width or height of zero'
     246          while x >= width:
     247              x -= width
     248              y += 1
     249          while y >= height:
     250              if self._scrollMode == 'scroll':
     251                  y -= 1
     252                  self.scroll(0, -1)
     253              elif self._scrollMode == 'error':
     254                  # reset the cursor on error
     255                  self._cursor = (0, 0)
     256                  raise TDLError('Cursor has reached the end of the console')
     257          return (x, y)
     258
     259 -    def _lockColors(self, forceUpdate=False):
     260          """Make sure the color options on the root console match ths instance"""
     261          if self.console._lockColors is not self or forceUpdate:
     262              self.console._lockColors = self
     263              _lib.TCOD_console_set_default_background(self.console, self.bgcolor)
     264              _lib.TCOD_console_set_default_foreground(self.console, self.fgcolor)
     265              #
     266
     267 -    def setMode(self, mode):
     268          """Configure how this console will react to the cursor writing past the
     269          end if the console.
     270
     271          This is for methods that use the virtual cursor, such as L{printStr}.
     272
     273          @type mode: string
     274          @param mode: Possible settings are:
     275
     276                        - 'error' - A TDLError will be raised once the cursor
     277                          reaches the end of the console.  Everything up until
     278                          the error will still be drawn.
     279
     280                          This is the default setting.
     281
     282                        - 'scroll' - The console will scroll up as stuff is
     283                          written to the end.
     284
     285                          You can restrict the region with L{tdl.Window} when
     286                          doing this.
     287          """
     288          MODES = ['error', 'scroll']
     289          if mode.lower() not in MODES:
     290              raise TDLError('mode must be one of %s, got %s' % (MODES, repr(mode)))
     291          self._scrollMode = mode.lower()
     292
     293 -    def setColors(self, fg=None, bg=None):
     294          """Sets the colors to be used with the L{printStr} function.
     295
     296          Values of None will only leave the current values unchanged.
     297          """
     298          if self.console._lockColors is self:
     299              self.console._lockColors = None
     300          if fg is not None:
     301              self._fgcolor = _formatColor(fg)
     302          if bg is not None:
     303              self._bgcolor = _formatColor(bg)
     304
     305 -    def printStr(self, string):
     306          """Print a string at the virtual cursor.
     307
     308          Handles special characters such as '\\n' and '\\r'.
     309          Printing past the bottom of the console will scroll everying upwards.
     310
     311          Colors can be set with L{setColors} and the virtual cursor can be moved
     312          with L{move}.
     313
     314          @type string: string
     315          @param string:
     316          """
     317          x, y = self._cursor
     318          for char in string:
     319              if char == '\n': # line break
     320                  x = 0
     321                  y += 1
     322                  continue
     323              if char == '\r': # return
     324                  x = 0
     325                  continue
     326              x, y = self._normalizeCursor(x, y)
     327              self.drawChar(x, y, char, self._fgcolor, self._bgcolor)
     328              x += 1
     329          self._cursor = (x, y)
     330
     331 -    def write(self, string):
     332          """This method mimics basic file-like behaviour.
     333
     334          Because of this method you can replace sys.stdout or sys.stderr with
     335          a L{Typewriter} instance.
     336
     337          This is a convoluted process and behaviour seen now can be excepted to
     338          change on later versions.
     339
     340          @type string: string
     341          """
     342          # some 'basic' line buffer stuff.
     343          # there must be an easier way to do this.  The textwrap module didn't
     344          # help much.
     345          x, y = self._normalizeCursor(*self._cursor)
     346          width, height = self.getSize()
     347          wrapper = textwrap.TextWrapper(initial_indent=(' '*x), width=width)
     348          writeLines = []
     349          for line in string.split('\n'):
     350              if line:
     351                  writeLines += wrapper.wrap(line)
     352                  wrapper.initial_indent = ''
     353              else:
     354                  writeLines.append([])
     355
     356          for line in writeLines:
     357              x, y = self._normalizeCursor(x, y)
     358              self.drawStr(x, y, line[x:], self._fgcolor, self._bgcolor)
     359              y += 1
     360              x = 0
     361          y -= 1
     362          self._cursor = (x, y)
     363
     364 -    def drawChar(self, x, y, char, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     365          """Draws a single character.
     366
     367          @type x: int
     368          @param x: X coordinate to draw at.
     369          @type y: int
     370          @param y: Y coordinate to draw at.
     371
     372          @type char: int, string, or None
     373          @param char: Should be an integer, single character string, or None.
     374
     375                       You can set the char parameter as None if you only want to change
     376                       the colors of the tile.
     377
     378          @type fgcolor: (r, g, b) or None
     379          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     380                          integers ranging 0-255 or None.
     381
     382                          None will keep the current color at this position unchanged.
     383          @type bgcolor: (r, g, b) or None
     384          @param bgcolor: Background color.  See fgcolor
     385
     386          @raise AssertionError: Having x or y values that can't be placed inside
     387                                 of the console will raise an AssertionError.
     388                                 You can use always use ((x, y) in console) to
     389                                 check if a tile is drawable.
     390          """
     391
     392          assert _verify_colors(fgcolor, bgcolor)
     393          x, y = self._normalizePoint(x, y)
     394          x, y = ctypes.c_int(x), ctypes.c_int(y)
     395          self._setChar(x, y, _formatChar(char),
     396                        _formatColor(fgcolor), _formatColor(bgcolor))
     397
     398 -    def drawStr(self, x, y, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     399          """Draws a string starting at x and y.  Optinally colored.
     400
     401          A string that goes past the right side will wrap around.  A string
     402          wraping to below the console will raise a L{TDLError} but will still be
     403          written out.  This means you can safely ignore the errors with a
     404          try... except block if you're fine with partily written strings.
     405
     406          \\r and \\n are drawn on the console as normal character tiles.  No
     407          special encoding is done and any string will translate to the character
     408          table as is.
     409
     410          For a string drawing operation that respects special characters see the
     411          L{Typewriter} class.
     412
     413          @type x: int
     414          @param x: X coordinate to draw at.
     415          @type y: int
     416          @param y: Y coordinate to draw at.
     417
     418          @type string: string or iterable
     419          @param string: Can be a string or an iterable of numbers.
     420
     421                         Special characters are ignored and rendered as any other
     422                         character.
     423
     424          @type fgcolor: (r, g, b) or None
     425          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     426                          integers ranging 0-255 or None.
     427
     428                          None will keep the current color at this position unchanged.
     429          @type bgcolor: (r, g, b) or None
     430          @param bgcolor: Background color.  See fgcolor
     431
     432          @raise AssertionError: Having x or y values that can't be placed inside
     433                                 of the console will raise an AssertionError.
     434
     435                                 You can use always use ((x, y) in console) to
     436                                 check if a tile is drawable.
     437          """
     438
     439          x, y = self._normalizePoint(x, y)
     440          assert _verify_colors(fgcolor, bgcolor)
     441          fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
     442          width, height = self.getSize()
     443          batch = [] # prepare a batch operation
     444          def _drawStrGen(x=x, y=y, string=string, width=width, height=height):
     445              """Generator for drawStr
     446
     447              Iterates over ((x, y), ch) data for _setCharBatch, raising an
     448              error if the end of the console is reached.
     449              """
     450              for char in string:
     451                  if y == height:
     452                      raise TDLError('End of console reached.')
     453                  #batch.append(((x, y), _formatChar(char))) # ((x, y), ch)
     454                  yield((x, y), _formatChar(char))
     455                  x += 1 # advance cursor
     456                  if x == width: # line break
     457                      x = 0
     458                      y += 1
     459          self._setCharBatch(_drawStrGen(), fgcolor, bgcolor)
     460
     461 -    def drawRect(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     462          """Draws a rectangle starting from x and y and extending to width and height.
     463
     464          If width or height are None then it will extend to the edge of the console.
     465
     466          @type x: int
     467          @param x: x coordinate to draw at.
     468          @type y: int
     469          @param y: y coordinate to draw at.
     470
     471          @type width: int or None
     472          @param width: Width of the rectangle.
     473
     474                        Can be None to extend to the bottom right of the
     475                        console or can be a negative number to be sized reltive
     476                        to the total size of the console.
     477          @type height: int or None
     478          @param height: Height of the rectangle.  See width.
     479
     480          @type string: int, string, or None
     481          @param string: Should be an integer, single character string, or None.
     482
     483                         You can set the char parameter as None if you only want
     484                         to change the colors of an area.
     485
     486          @type fgcolor: (r, g, b) or None
     487          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     488                          integers ranging 0-255 or None.
     489
     490                          None will keep the current color at this position unchanged.
     491          @type bgcolor: (r, g, b) or None
     492          @param bgcolor: Background color.  See fgcolor
     493
     494          @raise AssertionError: Having x or y values that can't be placed inside
     495                                 of the console will raise an AssertionError.
     496
     497                                 You can use always use ((x, y) in console) to
     498                                 check if a tile is drawable.
     499          """
     500          x, y, width, height = self._normalizeRect(x, y, width, height)
     501          assert _verify_colors(fgcolor, bgcolor)
     502          fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
     503          char = _formatChar(string)
     504          # use itertools to make an x,y grid
     505          # using ctypes here reduces type converstions later
     506          grid = itertools.product((ctypes.c_int(x) for x in range(x, x + width)),
     507                                   (ctypes.c_int(y) for y in range(y, y + height)))
     508          # zip the single character in a batch variable
     509          batch = zip(grid, itertools.repeat(char, width * height))
     510          self._setCharBatch(batch, fgcolor, bgcolor, nullChar=(char is None))
     511
     512 -    def drawFrame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     513          """Similar to L{drawRect} but only draws the outline of the rectangle.
     514
     515          @type x: int
     516          @param x: x coordinate to draw at.
     517          @type y: int
     518          @param y: y coordinate to draw at.
     519
     520          @type width: int or None
     521          @param width: Width of the rectangle.
     522
     523                        Can be None to extend to the bottom right of the
     524                        console or can be a negative number to be sized reltive
     525                        to the total size of the console.
     526          @type height: int or None
     527          @param height: Height of the rectangle.  See width.
     528
     529          @type string: int, string, or None
     530          @param string: Should be an integer, single character string, or None.
     531
     532                         You can set the char parameter as None if you only want
     533                         to change the colors of an area.
     534
     535          @type fgcolor: (r, g, b) or None
     536          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     537                          integers ranging 0-255 or None.
     538
     539                          None will keep the current color at this position unchanged.
     540          @type bgcolor: (r, g, b) or None
     541          @param bgcolor: Background color.  See fgcolor
     542
     543          @raise AssertionError: Having x or y values that can't be placed inside
     544                                 of the console will raise an AssertionError.
     545
     546                                 You can use always use ((x, y) in console) to
     547                                 check if a tile is drawable.
     548          """
     549          x, y, width, height = self._normalizeRect(x, y, width, height)
     550          assert _verify_colors(fgcolor, bgcolor)
     551          fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
     552          char = _formatChar(string)
     553          if width == 1 or height == 1: # it's just a single width line here
     554              return self.drawRect(x, y, width, height, char, fgcolor, bgcolor)
     555
     556          # draw sides of frame with drawRect
     557          self.drawRect(x, y, 1, height, char, fgcolor, bgcolor)
     558          self.drawRect(x, y, width, 1, char, fgcolor, bgcolor)
     559          self.drawRect(x + width - 1, y, 1, height, char, fgcolor, bgcolor)
     560          self.drawRect(x, y + height - 1, width, 1, char, fgcolor, bgcolor)
     561
     562 -    def blit(self, source, x=0, y=0, width=None, height=None, srcX=0, srcY=0):
     563          """Blit another console or Window onto the current console.
     564
     565          By default it blits the entire source to the topleft corner.
     566
     567          @type source: L{Console} or L{Window}
     568          @param source: Source window can be a L{Console} or L{Window} instance.
     569                         It can even blit to itself without any problems.
     570
     571          @type x: int
     572          @param x: X coordinate to blit to.
     573          @type y: int
     574          @param y: Y coordinate to blit to.
     575
     576          @type width: int or None
     577          @param width: Width of the rectangle.
     578
     579                        Can be None to extend as far as possible to the
     580                        bottom right corner of the blit area or can be a negative
     581                        number to be sized reltive to the total size of the
     582                        B{destination} console.
     583          @type height: int or None
     584          @param height: Height of the rectangle.  See width.
     585
     586          @type srcX: int
     587          @param srcX: The source consoles x coordinate to blit from.
     588          @type srcY: int
     589          @param srcY: The source consoles y coordinate to blit from.
     590          """
     591          # hardcode alpha settings for now
     592          fgalpha=1.0
     593          bgalpha=1.0
     594
     595          assert isinstance(source, (Console, Window)), "source muse be a Window or Console instance"
     596
     597          # handle negative indexes and rects
     598          # negative width and height will be set realtive to the destination
     599          # and will also be clamped to the smallest Console
     600          x, y, width, height = self._normalizeRect(x, y, width, height)
     601          srcX, srcY, width, height = source._normalizeRect(srcX, srcY, width, height)
     602
     603          # translate source and self if any of them are Window instances
     604          srcX, srcY = source._translate(srcX, srcY)
     605          source = source.console
     606          x, y = self._translate(x, y)
     607          self = self.console
     608
     609          if self == source:
     610              # if we are the same console then we need a third console to hold
     611              # onto the data, otherwise it tries to copy into itself and
     612              # starts destroying everything
     613              tmp = Console(width, height)
     614              _lib.TCOD_console_blit(source, srcX, srcY, width, height, tmp, 0, 0, fgalpha, bgalpha)
     615              _lib.TCOD_console_blit(tmp, 0, 0, width, height, self, x, y, fgalpha, bgalpha)
     616          else:
     617              _lib.TCOD_console_blit(source, srcX, srcY, width, height, self, x, y, fgalpha, bgalpha)
     618
     619 -    def getCursor(self):
     620          """Return the virtual cursor position.
     621
     622          @rtype: (x, y)
     623          @return: Returns (x, y) a 2-integer tuple containing where the next
     624                   L{addChar} or L{addStr} will start at.
     625
     626                   This can be changed with the L{move} method."""
     627          x, y = self._cursor
     628          width, height = self.parent.getSize()
     629          while x >= width:
     630              x -= width
     631              y += 1
     632          if y >= height and self.scrollMode == 'scroll':
     633              y = height - 1
     634          return x, y
     635
     636 -    def getSize(self):
     637          """Return the size of the console as (width, height)
     638
     639          @rtype: (width, height)
     640          """
     641          return self.width, self.height
     642
     643 -    def __iter__(self):
     644          """Return an iterator with every possible (x, y) value for this console.
     645
     646          It goes without saying that working on the console this way is a
     647          slow process, especially for Python, and should be minimized.
     648          @rtype: iter((x, y), ...)
     649          """
     650          return itertools.product(range(self.width), range(self.height))
     651
     652 -    def move(self, x, y):
     653          """Move the virtual cursor.
     654
     655          @type x: int
     656          @param x: X position to place the cursor.
     657          @type y: int
     658          @param y: Y position to place the cursor.
     659          """
     660          self._cursor = self._normalizePoint(x, y)
     661
     662 -    def scroll(self, x, y):
     663          """Scroll the contents of the console in the direction of x,y.
     664
     665          Uncovered areas will be cleared.
     666          Does not move the virutal cursor.
     667          @type x: int
     668          @param x: Distance to scroll along x-axis
     669          @type y: int
     670          @param y: Distance to scroll along y-axis
     671          @rtype: iter((x, y), ...)
     672          @return: Iterates over the (x, y) of any tile uncovered after scrolling.
     673          """
     674          assert isinstance(x, _INTTYPES), "x must be an integer, got %s" % repr(x)
     675          assert isinstance(y, _INTTYPES), "y must be an integer, got %s" % repr(x)
     676          def getSlide(x, length):
     677              """get the parameters needed to scroll the console in the given
     678              direction with x
     679              returns (x, length, srcx)
     680              """
     681              if x > 0:
     682                  srcx = 0
     683                  length -= x
     684              elif x < 0:
     685                  srcx = abs(x)
     686                  x = 0
     687                  length -= srcx
     688              else:
     689                  srcx = 0
     690              return x, length, srcx
     691          def getCover(x, length):
     692              """return the (x, width) ranges of what is covered and uncovered"""
     693              cover = (0, length) # everything covered
     694              uncover = None  # nothing uncovered
     695              if x > 0: # left side uncovered
     696                  cover = (x, length - x)
     697                  uncover = (0, x)
     698              elif x < 0: # right side uncovered
     699                  x = abs(x)
     700                  cover = (0, length - x)
     701                  uncover = (length - x, x)
     702              return cover, uncover
     703
     704          width, height = self.getSize()
     705          if abs(x) >= width or abs(y) >= height:
     706              return self.clear() # just clear the console normally
     707
     708          # get the ranges of the areas that will be uncovered
     709          coverX, uncoverX = getCover(x, width)
     710          coverY, uncoverY = getCover(y, height)
     711          # so at this point we know that coverX and coverY makes a rect that
     712          # encases the area that we end up blitting to.  uncoverX/Y makes a
     713          # rect in the corner of the uncovered area.  So we need to combine
     714          # the uncoverX/Y with coverY/X to make what's left of the uncovered
     715          # area.  Explaining it makes it mush easier to do now.
     716
     717          # But first we need to blit.
     718          x, width, srcx = getSlide(x, width)
     719          y, height, srcy = getSlide(y, height)
     720          self.blit(self, x, y, width, height, srcx, srcy)
     721
     722          if uncoverX: # clear sides (0x20 is space)
     723              self.drawRect(uncoverX[0], coverY[0], uncoverX[1], coverY[1], 0x20, 0x000000, 0x000000)
     724          if uncoverY: # clear top/bottom
     725              self.drawRect(coverX[0], uncoverY[0], coverX[1], uncoverY[1], 0x20, 0x000000, 0x000000)
     726          if uncoverX and uncoverY: # clear corner
     727              self.drawRect(uncoverX[0], uncoverY[0], uncoverX[1], uncoverY[1], 0x20, 0x000000, 0x000000)
     728
     729 -    def getChar(self, x, y):
     730          """Return the character and colors of a tile as (ch, fg, bg)
     731
     732          This method runs very slowly as is not recommended to be called
     733          frequently.
     734
     735          @rtype: (int, (r, g, b), (r, g, b))
     736          @returns: Returns a 3-item tuple.  The first item is an integer of the
     737                    character at the position (x, y) the second and third are the
     738                    foreground and background colors respectfully.
     739          """
     740          raise NotImplementedError('Method here only exists for the docstring')
     741
     742 -    def __contains__(self, position):
     743          """Use ((x, y) in console) to check if a position is drawable on this console.
     744          """
     745          x, y = position
     746          return (0 <= x < self.width) and (0 <= y < self.height)
     747
     748 -class Console(_MetaConsole):
     749      """Contains character and color data and can be drawn to.
     750
     751      The console created by the L{tdl.init} function is the root console and is the
     752      console that is rendered to the screen with L{flush}.
     753
     754      Any console created from the Console class is an off-screen console that
     755      can be drawn on before being L{blit} to the root console.
     756      """
     757
     758      __slots__ = ('_as_parameter_', '_typewriter')
     759
     760 -    def __init__(self, width, height):
     761          """Create a new offscreen console.
     762
     763          @type width: int
     764          @param width: Width of the console in tiles
     765          @type height: int
     766          @param height: Height of the console in tiles
     767          """
     768          _MetaConsole.__init__(self)
     769          if not _rootinitialized:
     770              raise TDLError('Can not create Console\'s before tdl.init')
     771          self._as_parameter_ = _lib.TCOD_console_new(width, height)
     772          self.console = self
     773          self.width = width
     774          self.height = height
     775          self._typewriter = None # "typewriter lock", makes sure the colors are set to the typewriter
     776          # will be phased out with the Typewriter class
     777
     778      @classmethod
     779 -    def _newConsole(cls, console):
     780          """Make a Console instance, from a console ctype"""
     781          self = cls.__new__(cls)
     782          _MetaConsole.__init__(self)
     783          self._as_parameter_ = console
     784          self.console = self
     785          self.width = _lib.TCOD_console_get_width(self)
     786          self.height = _lib.TCOD_console_get_height(self)
     787          self._typewriter = None
     788          return self
     789
     790 -    def __del__(self):
     791          """
     792          If the main console is garbage collected then the window will be closed as well
     793          """
     794          # If this is the root console the window will close when collected
     795          try:
     796              if isinstance(self._as_parameter_, ctypes.c_void_p):
     797                  global _rootinitialized, _rootConsoleRef
     798                  _rootinitialized = False
     799                  _rootConsoleRef = None
     800              _lib.TCOD_console_delete(self)
     801          except StandardError:
     802              pass # I forget why I put this here but I'm to afraid to delete it
     803
     804 -    def __copy__(self):
     805          # make a new class and blit
     806          clone = self.__class__(self.width, self.height)
     807          clone.blit(self)
     808          return clone
     809
     810 -    def __getstate__(self):
     811          # save data from getChar
     812          data = [self.getChar(x, y) for x,y in
     813                  itertools.product(range(self.width), range(self.height))]
     814          return self.width, self.height, data
     815
     816 -    def __setstate__(self, state):
     817          # make console from __init__ and unpack a getChar array
     818          width, height, data = state
     819          self.__init__(width, height)
     820          for (x, y), graphic in zip(itertools.product(range(width),
     821                                                       range(height)), data):
     822              self.drawChar(x, y, *graphic)
     823
     824 -    def _replace(self, console):
     825          """Used internally
     826
     827          Mostly used just to replace this Console object with the root console
     828          If another Console object is used then they are swapped
     829          """
     830          if isinstance(console, Console):
     831              self._as_parameter_, console._as_parameter_ = \
     832                console._as_parameter_, self._as_parameter_ # swap tcod consoles
     833          else:
     834              self._as_parameter_ = console
     835          self.width = _lib.TCOD_console_get_width(self)
     836          self.height = _lib.TCOD_console_get_height(self)
     837          return self
     838
     839 -    def _translate(self, x, y):
     840          """Convertion x and y to their position on the root Console for this Window
     841
     842          Because this is a Console instead of a Window we return the paramaters
     843          untouched"""
     844          return x, y
     845
     846 -    def clear(self, fgcolor=(0, 0, 0), bgcolor=(0, 0, 0)):
     847          """Clears the entire Console.
     848
     849          @type fgcolor: (r, g, b)
     850          @param fgcolor: Foreground color.
     851
     852                          Must be a 3-item list with integers that range 0-255.
     853
     854                          Unlike most other operations you cannot use None here.
     855          @type bgcolor: (r, g, b)
     856          @param bgcolor: Background color.  See fgcolor.
     857          """
     858          assert _verify_colors(fgcolor, bgcolor)
     859          assert fgcolor and bgcolor, 'Can not use None with clear'
     860          self._typewriter = None
     861          _lib.TCOD_console_set_default_background(self, _formatColor(bgcolor))
     862          _lib.TCOD_console_set_default_foreground(self, _formatColor(fgcolor))
     863          _lib.TCOD_console_clear(self)
     864
     865 -    def _setChar(self, x, y, char, fgcolor=None, bgcolor=None, bgblend=1):
     866          """
     867          Sets a character.
     868          This is called often and is designed to be as fast as possible.
     869
     870          Because of the need for speed this function will do NO TYPE CHECKING
     871          AT ALL, it's up to the drawing functions to use the functions:
     872          _formatChar and _formatColor before passing to this."""
     873          # buffer values as ctypes objects
     874          console = self._as_parameter_
     875
     876          if char is not None and fgcolor is not None and bgcolor is not None:
     877              _setcharEX(console, x, y, char, fgcolor, bgcolor)
     878              return
     879          if char is not None:
     880              _setchar(console, x, y, char)
     881          if fgcolor is not None:
     882              _setfore(console, x, y, fgcolor)
     883          if bgcolor is not None:
     884              _setback(console, x, y, bgcolor, bgblend)
     885
     886 -    def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1, nullChar=False):
     887          """
     888          Try to perform a batch operation otherwise fall back to _setChar.
     889          If fgcolor and bgcolor are defined then this is faster but not by very
     890          much.
     891
     892          batch is a iterable of [(x, y), ch] items
     893          """
     894          if fgcolor and not nullChar:
     895              # buffer values as ctypes objects
     896              self._typewriter = None # clear the typewriter as colors will be set
     897              console = self._as_parameter_
     898              bgblend = ctypes.c_int(bgblend)
     899
     900              if not bgcolor:
     901                  bgblend = 0
     902              else:
     903                  _lib.TCOD_console_set_default_background(console, bgcolor)
     904              _lib.TCOD_console_set_default_foreground(console, fgcolor)
     905              _putChar = _lib.TCOD_console_put_char # remove dots and make local
     906              for (x, y), char in batch:
     907                  _putChar(console, x, y, char, bgblend)
     908          else:
     909              for (x, y), char in batch:
     910                  self._setChar(x, y, char, fgcolor, bgcolor, bgblend)
     911
     912 -    def getChar(self, x, y):
     913          # inherit docstring
     914          x, y = self._normalizePoint(x, y)
     915          char = _lib.TCOD_console_get_char(self, x, y)
     916          bgcolor = _lib.TCOD_console_get_char_background_wrapper(self, x, y)
     917          fgcolor = _lib.TCOD_console_get_char_foreground_wrapper(self, x, y)
     918          return char, tuple(fgcolor), tuple(bgcolor)
     919
     920 -    def __repr__(self):
     921          return "<Console (Width=%i Height=%i)>" % (self.width, self.height)
     922
     923
     924 -class Window(_MetaConsole):
     925      """A Window contains a small isolated part of a Console.
     926
     927      Drawing on the Window draws on the Console.
     928
     929      Making a Window and setting its width or height to None will extend it to
     930      the edge of the console.
     931      """
     932
     933      __slots__ = ('parent', 'x', 'y')
     934
     935 -    def __init__(self, console, x, y, width, height):
     936          """Isolate part of a L{Console} or L{Window} instance.
     937
     938          @type console: L{Console} or L{Window}
     939          @param console: The parent object which can be a L{Console} or another
     940                          L{Window} instance.
     941
     942          @type x: int
     943          @param x: X coordinate to place the Window.
     944
     945                    This follows the normal rules for indexing so you can use a
     946                    negative integer to place the Window relative to the bottom
     947                    right of the parent Console instance.
     948          @type y: int
     949          @param y: Y coordinate to place the Window.
     950
     951                    See x.
     952
     953          @type width: int or None
     954          @param width: Width of the Window.
     955
     956                        Can be None to extend as far as possible to the
     957                        bottom right corner of the parent Console or can be a
     958                        negative number to be sized reltive to the Consoles total
     959                        size.
     960          @type height: int or None
     961          @param height: Height of the Window.
     962
     963                         See width.
     964          """
     965          _MetaConsole.__init__(self)
     966          assert isinstance(console, (Console, Window)), 'console parameter must be a Console or Window instance, got %s' % repr(console)
     967          self.parent = console
     968          self.x, self.y, self.width, self.height = console._normalizeRect(x, y, width, height)
     969          if isinstance(console, Console):
     970              self.console = console
     971          else:
     972              self.console = self.parent.console
     973
     974 -    def _translate(self, x, y):
     975          """Convertion x and y to their position on the root Console"""
     976          # we add our position relative to our parent and then call then next parent up
     977          return self.parent._translate((x + self.x), (y + self.y))
     978
     979 -    def clear(self, fgcolor=(0, 0, 0), bgcolor=(0, 0, 0)):
     980          """Clears the entire Window.
     981
     982          @type fgcolor: (r, g, b)
     983          @param fgcolor: Foreground color.
     984
     985                          Must be a 3-item list with integers that range 0-255.
     986
     987                          Unlike most other operations you can not use None here.
     988          @type bgcolor: (r, g, b)
     989          @param bgcolor: Background color.  See fgcolor.
     990          """
     991          assert _verify_colors(fgcolor, bgcolor)
     992          assert fgcolor and bgcolor, 'Can not use None with clear'
     993          self.drawRect(0, 0, None, None, 0x20, fgcolor, bgcolor)
     994
     995 -    def _setChar(self, x, y, char=None, fgcolor=None, bgcolor=None, bgblend=1):
     996          self.parent._setChar((x + self.x), (y + self.y), char, fgcolor, bgcolor, bgblend)
     997
     998 -    def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1):
     999          myX = self.x # remove dots for speed up
    1000          myY = self.y
    1001          self.parent._setCharBatch((((x + myX, y + myY), ch) for ((x, y), ch) in batch),
    1002                                    fgcolor, bgcolor, bgblend)
    1003
    1004
    1005 -    def drawChar(self, x, y, char, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
    1006          # inherit docstring
    1007          x, y = self._normalizePoint(x, y)
    1008          self.parent.drawChar(x + self.x, y + self.y, char, fgcolor, bgcolor)
    1009
    1010 -    def drawRect(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
    1011          # inherit docstring
    1012          x, y, width, height = self._normalizeRect(x, y, width, height)
    1013          self.parent.drawRect(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
    1014
    1015 -    def drawFrame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
    1016          # inherit docstring
    1017          x, y, width, height = self._normalizeRect(x, y, width, height)
    1018          self.parent.drawFrame(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
    1019
    1020 -    def getChar(self, x, y):
    1021          # inherit docstring
    1022          x, y = self._normalizePoint(x, y)
    1023          return self.console.getChar(self._translate(x, y))
    1024
    1025 -    def __repr__(self):
    1026          return "<Window(X=%i Y=%i Width=%i Height=%i)>" % (self.x, self.y,
    1027                                                            self.width,
    1028                                                            self.height)
    1029
    1030
    1031 -def init(width, height, title=None, fullscreen=False, renderer='OPENGL'):
    1032      """Start the main console with the given width and height and return the
    1033      root console.
    1034
    1035      Call the consoles drawing functions.  Then remember to use L{tdl.flush} to
    1036      make what's drawn visible on the console.
    1037
    1038      @type width: int
    1039      @param width: width of the root console (in tiles)
    1040
    1041      @type height: int
    1042      @param height: height of the root console (in tiles)
    1043
    1044      @type title: string
    1045      @param title: Text to display as the window title.
    1046
    1047                    If left None it defaults to the running scripts filename.
    1048
    1049      @type fullscreen: boolean
    1050      @param fullscreen: Can be set to True to start in fullscreen mode.
    1051
    1052      @type renderer: string
    1053      @param renderer: Can be one of 'GLSL', 'OPENGL', or 'SDL'.
    1054
    1055                       Due to way Python works you're unlikely to see much of an
    1056                       improvement by using 'GLSL' or 'OPENGL' as most of the
    1057                       time Python is slow interacting with the console and the
    1058                       rendering itself is pretty fast even on 'SDL'.
    1059
    1060      @rtype: L{Console}
    1061      @return: The root console.  Only what is drawn on the root console is
    1062               what's visible after a call to L{tdl.flush}.
    1063               After the root console is garbage collected, the window made by
    1064               this function will close.
    1065      """
    1066      RENDERERS = {'GLSL': 0, 'OPENGL': 1, 'SDL': 2}
    1067      global _rootinitialized, _rootConsoleRef
    1068      if not _fontinitialized: # set the default font to the one that comes with tdl
    1069          setFont(_unpackfile('terminal8x8.png'), None, None, True, True)
    1070
    1071      if renderer.upper() not in RENDERERS:
    1072          raise TDLError('No such render type "%s", expected one of "%s"' % (renderer, '", "'.join(RENDERERS)))
    1073      renderer = RENDERERS[renderer.upper()]
    1074
    1075      # If a console already exists then make a clone to replace it
    1076      if _rootConsoleRef and _rootConsoleRef():
    1077          oldroot = _rootConsoleRef()
    1078          rootreplacement = Console(oldroot.width, oldroot.height)
    1079          rootreplacement.blit(oldroot)
    1080          oldroot._replace(rootreplacement)
    1081          del rootreplacement
    1082
    1083      if title is None: # use a default title
    1084          if sys.argv:
    1085              # Use the script filename as the title.
    1086              title = os.path.basename(sys.argv[0])
    1087          else:
    1088              title = 'python-tdl'
    1089
    1090      _lib.TCOD_console_init_root(width, height, _encodeString(title), fullscreen, renderer)
    1091
    1092      #event.get() # flush the libtcod event queue to fix some issues
    1093      # issues may be fixed already
    1094
    1095      event._eventsflushed = False
    1096      _rootinitialized = True
    1097      rootconsole = Console._newConsole(ctypes.c_void_p())
    1098      _rootConsoleRef = weakref.ref(rootconsole)
    1099
    1100      return rootconsole
    1101
    1102 -def flush():
    1103      """Make all changes visible and update the screen.
    1104
    1105      Remember to call this function after drawing operations.
    1106      Calls to flush will enfore the frame rate limit set by L{tdl.setFPS}.
    1107
    1108      This function can only be called after L{tdl.init}
    1109      """
    1110      if not _rootinitialized:
    1111          raise TDLError('Cannot flush without first initializing with tdl.init')
    1112
    1113      _lib.TCOD_console_flush()
    1114
    1115 -def setFont(path, columns=None, rows=None, columnFirst=False,
    1116              greyscale=False, altLayout=False):
    1117      """Changes the font to be used for this session.
    1118      This should be called before L{tdl.init}
    1119
    1120      If the font specifies its size in its filename (i.e. font_NxN.png) then this
    1121      function can auto-detect the tileset formatting and the parameters columns
    1122      and rows can be left None.
    1123
    1124      While it's possible you can change the font mid program it can sometimes
    1125      break in rare circumstances.  So use caution when doing this.
    1126
    1127      @type path: string
    1128      @param path: Must be a string filepath where a bmp or png file is found.
    1129
    1130      @type columns: int
    1131      @param columns: Number of columns in the tileset.
    1132
    1133                      Can be left None for auto-detection.
    1134
    1135      @type rows: int
    1136      @param rows: Number of rows in the tileset.
    1137
    1138                   Can be left None for auto-detection.
    1139
    1140      @type columnFirst: boolean
    1141      @param columnFirst: Defines if the characer order goes along the rows or
    1142                          colomns.
    1143                          It should be True if the charater codes 0-15 are in the
    1144                          first column.
    1145                          And should be False if the characters 0-15
    1146                          are in the first row.
    1147
    1148      @type greyscale: boolean
    1149      @param greyscale: Creates an anti-aliased font from a greyscale bitmap.
    1150                        Otherwise it uses the alpha channel for anti-aliasing.
    1151
    1152                        Unless you actually need anti-aliasing from a font you
    1153                        know uses a smooth greyscale channel you should leave
    1154                        this on False.
    1155
    1156      @type altLayout: boolean
    1157      @param altLayout: An alternative layout with space in the upper left
    1158                        corner.
    1159                        The colomn parameter is ignored if this is True,
    1160                        find examples of this layout in the font/libtcod/
    1161                        directory included with the python-tdl source.
    1162
    1163      @raise TDLError: Will be raised if no file is found at path or if auto-
    1164                       detection fails.
    1165
    1166      @note: A png file that's been optimized can fail to load correctly on
    1167             MAC OS X creating a garbled mess when rendering.
    1168             Don't use a program like optipng or just use bmp files instead if
    1169             you want your program to work on macs.
    1170      """
    1171      # put up some constants that are only used here
    1172      FONT_LAYOUT_ASCII_INCOL = 1
    1173      FONT_LAYOUT_ASCII_INROW = 2
    1174      FONT_TYPE_GREYSCALE = 4
    1175      FONT_LAYOUT_TCOD = 8
    1176      global _fontinitialized
    1177      _fontinitialized = True
    1178      flags = 0
    1179      if altLayout:
    1180          flags |= FONT_LAYOUT_TCOD
    1181      elif columnFirst:
    1182          flags |= FONT_LAYOUT_ASCII_INCOL
    1183      else:
    1184          flags |= FONT_LAYOUT_ASCII_INROW
    1185      if greyscale:
    1186          flags |= FONT_TYPE_GREYSCALE
    1187      if not os.path.exists(path):
    1188          raise TDLError('no file exists at: "%s"' % path)
    1189      path = os.path.abspath(path)
    1190
    1191      # and the rest is the auto-detect script
    1192      imgSize = _getImageSize(path) # try to find image size
    1193      if imgSize:
    1194          imgWidth, imgHeight = imgSize
    1195          # try to get font size from filename
    1196          match = re.match('.*?([0-9]+)[xX]([0-9]+)', os.path.basename(path))
    1197          if match:
    1198              fontWidth, fontHeight = match.groups()
    1199              fontWidth, fontHeight = int(fontWidth), int(fontHeight)
    1200
    1201              # estimate correct tileset size
    1202              estColumns, remC = divmod(imgWidth, fontWidth)
    1203              estRows, remR = divmod(imgHeight, fontHeight)
    1204              if remC or remR:
    1205                  warnings.warn("Font may be incorrectly formatted.")
    1206
    1207              if not columns:
    1208                  columns = estColumns
    1209              if not rows:
    1210                  rows = estRows
    1211          else:
    1212              # the font name excluded the fonts size
    1213              if not (columns and rows):
    1214                  # no matched font size and no tileset is given
    1215                  raise TDLError('%s has no font size in filename' % os.path.basename(path))
    1216
    1217          if columns and rows:
    1218              # confirm user set options
    1219              if (fontWidth * columns != imgWidth or
    1220                  fontHeight * rows != imgHeight):
    1221                  warnings.warn("setFont parameters are set as if the image size is (%d, %d) when the detected size is actually (%i, %i)"
    1222                               % (fontWidth * columns, fontHeight * rows,
    1223                                  imgWidth, imgHeight))
    1224      else:
    1225          warnings.warn("%s is probably not an image." % os.path.basename(path))
    1226
    1227      if not (columns and rows):
    1228          # didn't auto-detect
    1229          raise TDLError('Can not auto-detect the tileset of %s' % os.path.basename(path))
    1230
    1231      _lib.TCOD_console_set_custom_font(_encodeString(path), flags, columns, rows)
    1232
    1233 -def getFullscreen():
    1234      """Returns True if program is fullscreen.
    1235
    1236      @rtype: boolean
    1237      @return: Returns True if the window is in fullscreen mode.
    1238               Otherwise returns False.
    1239      """
    1240      if not _rootinitialized:
    1241          raise TDLError('Initialize first with tdl.init')
    1242      return _lib.TCOD_console_is_fullscreen()
    1243
    1244 -def setFullscreen(fullscreen):
    1245      """Changes the fullscreen state.
    1246
    1247      @type fullscreen: boolean
    1248      """
    1249      if not _rootinitialized:
    1250          raise TDLError('Initialize first with tdl.init')
    1251      _lib.TCOD_console_set_fullscreen(fullscreen)
    1252
    1253 -def setTitle(title):
    1254      """Change the window title.
    1255
    1256      @type title: string
    1257      """
    1258      if not _rootinitialized:
    1259          raise TDLError('Not initilized.  Set title with tdl.init')
    1260      _lib.TCOD_console_set_window_title(_encodeString(title))
    1261
    1262 -def screenshot(path=None):
    1263      """Capture the screen and save it as a png file
    1264
    1265      @type path: string
    1266      @param path: The filepath to save the screenshot.
    1267
    1268                   If path is None then the image will be placed in the current
    1269                   folder with the names:
    1270                   screenshot001.png, screenshot002.png, ...
    1271      """
    1272      if not _rootinitialized:
    1273          raise TDLError('Initialize first with tdl.init')
    1274      if isinstance(path, str):
    1275          _lib.TCOD_sys_save_screenshot(_encodeString(path))
    1276      elif path is None: # save to screenshot001.png, screenshot002.png, ...
    1277          filelist = os.listdir('.')
    1278          n = 1
    1279          filename = 'screenshot%.3i.png' % n
    1280          while filename in filelist:
    1281              n += 1
    1282              filename = 'screenshot%.3i.png' % n
    1283          _lib.TCOD_sys_save_screenshot(_encodeString(filename))
    1284      else: # assume file like obj
    1285          #save to temp file and copy to file-like obj
    1286          tmpname = os.tempnam()
    1287          _lib.TCOD_sys_save_screenshot(_encodeString(tmpname))
    1288          with tmpname as tmpfile:
    1289              path.write(tmpfile.read())
    1290          os.remove(tmpname)
    1291      #else:
    1292      #    raise TypeError('path is an invalid type: %s' % type(path))
    1293
    1294 -def setFPS(frameRate):
    1295      """Set the maximum frame rate.
    1296
    1297      @type frameRate: int
    1298      @param frameRate: Further calls to L{tdl.flush} will limit the speed of
    1299                        the program to run at <frameRate> frames per second. Can
    1300                        also be set to 0 to run without a limit.
    1301
    1302                        Defaults to None.
    1303      """
    1304      if frameRate is None:
    1305          frameRate = 0
    1306      assert isinstance(frameRate, _INTTYPES), 'frameRate must be an integer or None, got: %s' % repr(frameRate)
    1307      _lib.TCOD_sys_set_fps(frameRate)
    1308
    1309 -def getFPS():
    1310      """Return the current frames per second of the running program set by
    1311      L{setFPS}
    1312
    1313      @rtype: int
    1314      @return: Returns the frameRate set by setFPS.
    1315               If set to no limit, this will return 0.
    1316      """
    1317      return _lib.TCOD_sys_get_fps()
    1318
    1319 -def forceResolution(width, height):
    1320      """Change the fullscreen resoulution
    1321
    1322      @type width: int
    1323      @type height: int
    1324      """
    1325      _lib.TCOD_sys_force_fullscreen_resolution(width, height)
    1326
    1327  __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in
    1328             ['sys', 'os', 'ctypes', 'array', 'weakref', 'itertools', 'textwrap',
    1329              'struct', 're', 'warnings']] # remove modules from __all__
    1330  __all__ += ['_MetaConsole'] # keep this object public to show the documentation in epydoc
    1331
    1332  __license__ = "New BSD License"
    1333  __email__ = "4b796c65+pythonTDL@gmail.com"
    1334