summaryrefslogtreecommitdiff
path: root/ext/testlib/terminal.py
blob: 3afbb42a2f2fc7466ee40f7a721cf409cf5a387e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# Copyright (c) 2011 Advanced Micro Devices, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Author: Steve Reinhardt

import sys
import fcntl
import termios
import struct

# Intended usage example:
#
# if force_colors:
#    from m5.util.terminal import termcap
# elif no_colors:
#    from m5.util.terminal import no_termcap as termcap
# else:
#    from m5.util.terminal import tty_termcap as termcap
# print termcap.Blue + "This could be blue!" + termcap.Normal

# ANSI color names in index order
color_names = "Black Red Green Yellow Blue Magenta Cyan White".split()
default_separator = '='

# Character attribute capabilities.  Note that not all terminals
# support all of these capabilities, or support them
# differently/meaningfully.  For example:
#
# - In PuTTY (with the default settings), Dim has no effect, Standout
#   is the same as Reverse, and Blink does not blink but switches to a
#   gray background.
#
# Please feel free to add information about other terminals here.
#
capability_map = {
         'Bold': 'bold',
          'Dim': 'dim',
        'Blink': 'blink',
    'Underline': 'smul',
      'Reverse': 'rev',
     'Standout': 'smso',
       'Normal': 'sgr0'
}

capability_names = capability_map.keys()

def null_cap_string(s, *args):
    return ''

try:
    import curses
    curses.setupterm()
    def cap_string(s, *args):
        cap = curses.tigetstr(s)
        if cap:
            return curses.tparm(cap, *args)
        else:
            return ''
except:
    cap_string = null_cap_string

class ColorStrings(object):
    def __init__(self, cap_string):
        for i, c in enumerate(color_names):
            setattr(self, c, cap_string('setaf', i))
        for name, cap in capability_map.iteritems():
            setattr(self, name, cap_string(cap))

termcap = ColorStrings(cap_string)
no_termcap = ColorStrings(null_cap_string)

if sys.stdout.isatty():
    tty_termcap = termcap
else:
    tty_termcap = no_termcap

def get_termcap(use_colors = None):
    if use_colors:
        return termcap
    elif use_colors is None:
        # option unspecified; default behavior is to use colors iff isatty
        return tty_termcap
    else:
        return no_termcap

def terminal_size():
    '''Return the (width, heigth) of the terminal screen.'''
    h, w, hp, wp = struct.unpack('HHHH',
        fcntl.ioctl(0, termios.TIOCGWINSZ,
        struct.pack('HHHH', 0, 0, 0, 0)))
    return w, h

def separator(char=default_separator, color=None):
    '''
    Return a separator of the given character that is the length of the full
    width of the terminal screen.
    '''
    (w, h) = terminal_size()
    if color:
        return color + char*w + termcap.Normal
    else:
        return char*w

def insert_separator(inside, char=default_separator,
                     min_barrier=3, color=None):
    '''
    Place the given string inside of the separator. If it does not fit inside,
    expand the separator to fit it with at least min_barrier.

    .. seealso:: :func:`separator`
    '''
    # Use a bytearray so it's efficient to manipulate
    string = bytearray(separator(char, color=color))

    # Check if we can fit inside with at least min_barrier.
    gap = (len(string) - len(inside)) - min_barrier * 2
    if gap > 0:
        # We'll need to expand the string to fit us.
        string.extend([ char for _ in range(-gap)])
    # Emplace inside
    middle = ((len(string)-1)/2)
    start_idx = middle - len(inside)/2
    string[start_idx:len(inside)+start_idx] = inside
    return str(string)


if __name__ == '__main__':
    def test_termcap(obj):
        for c_name in color_names:
            c_str = getattr(obj, c_name)
            print c_str + c_name + obj.Normal
            for attr_name in capability_names:
                if attr_name == 'Normal':
                    continue
                attr_str = getattr(obj, attr_name)
                print attr_str + c_str + attr_name + " " + c_name + obj.Normal
            print obj.Bold + obj.Underline + \
                  c_name + "Bold Underline " + c_str + obj.Normal

    print "=== termcap enabled ==="
    test_termcap(termcap)
    print termcap.Normal
    print "=== termcap disabled ==="
    test_termcap(no_termcap)