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