Completed
Push — master ( f81e12...4517da )
by Kyle
01:17
created

Console.clear()   A

Complexity

Conditions 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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