untdl

Package untdl |
[frames] | no frames]

# Source Code for Package untdl

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