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