Completed
Push — master ( 19501a...b76b44 )
by Kyle
56s
created

_BaseConsole.move()   A

Complexity

Conditions 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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