Package tdl |
[frames] | no frames]

# Source Code for Package tdl

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