~ubuntu-branches/debian/sid/calibre/sid

« back to all changes in this revision

Viewing changes to src/calibre/utils/terminal.py

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2014-02-27 07:48:06 UTC
  • mto: This revision was merged to the branch mainline in revision 74.
  • Revision ID: package-import@ubuntu.com-20140227074806-64wdebb3ptosxhhx
Tags: upstream-1.25.0+dfsg
Import upstream version 1.25.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
93
93
    def __init__(self, stream):
94
94
        self.stream = stream or sys.stdout
95
95
        self.isatty = getattr(self.stream, 'isatty', lambda : False)()
96
 
        force_ansi = os.environ.has_key('CALIBRE_FORCE_ANSI')
 
96
        force_ansi = 'CALIBRE_FORCE_ANSI' in os.environ
97
97
        if not self.isatty and force_ansi:
98
98
            self.isatty = True
99
99
        self.isansi = force_ansi or not iswindows
100
 
        self.set_console = None
 
100
        self.set_console = self.write_console = None
 
101
        self.is_console = False
101
102
        if not self.isansi:
102
103
            try:
103
104
                import msvcrt
104
105
                self.msvcrt = msvcrt
105
106
                self.file_handle = msvcrt.get_osfhandle(self.stream.fileno())
106
 
                from ctypes import windll
107
 
                self.set_console = windll.kernel32.SetConsoleTextAttribute
 
107
                from ctypes import windll, wintypes, byref, c_wchar_p, c_size_t, POINTER, WinDLL
 
108
                mode = wintypes.DWORD(0)
 
109
                f = windll.kernel32.GetConsoleMode
 
110
                f.argtypes, f.restype = [wintypes.HANDLE, POINTER(wintypes.DWORD)], wintypes.BOOL
 
111
                if f(self.file_handle, byref(mode)):
 
112
                    # Stream is a console
 
113
                    self.set_console = windll.kernel32.SetConsoleTextAttribute
 
114
                    self.wcslen = crt().wcslen
 
115
                    self.wcslen.argtypes, self.wcslen.restype = [c_wchar_p], c_size_t
 
116
                    self.write_console = WinDLL('kernel32', use_last_error=True).WriteConsoleW
 
117
                    self.write_console.argtypes = [wintypes.HANDLE, wintypes.c_wchar_p, wintypes.DWORD, POINTER(wintypes.DWORD), wintypes.LPVOID]
 
118
                    self.write_console.restype = wintypes.BOOL
 
119
                    self.is_console = True
108
120
            except:
109
121
                pass
110
122
 
 
123
    def write_unicode_text(self, text, ignore_errors=False):
 
124
        ' Windows only method that writes unicode strings correctly to the windows console using the Win32 API '
 
125
        if self.is_console:
 
126
            from ctypes import wintypes, byref, c_wchar_p
 
127
            written = wintypes.DWORD(0)
 
128
            chunk = len(text)
 
129
            while text:
 
130
                t, text = text[:chunk], text[chunk:]
 
131
                wt = c_wchar_p(t)
 
132
                if not self.write_console(self.file_handle, wt, self.wcslen(wt), byref(written), None):
 
133
                    # Older versions of windows can fail to write large strings
 
134
                    # to console with WriteConsoleW (seen it happen on Win XP)
 
135
                    import ctypes, winerror
 
136
                    err = ctypes.get_last_error()
 
137
                    if err == winerror.ERROR_NOT_ENOUGH_MEMORY and chunk >= 128:
 
138
                        # Retry with a smaller chunk size (give up if chunk < 128)
 
139
                        chunk = chunk // 2
 
140
                        text = t + text
 
141
                        continue
 
142
                    if err == winerror.ERROR_GEN_FAILURE:
 
143
                        # On newer windows, this happens when trying to write
 
144
                        # non-ascii chars to the console and the console is set
 
145
                        # to use raster fonts (the default). In this case
 
146
                        # rather than failing, write an informative error
 
147
                        # message and the asciized version of the text.
 
148
                        print ('Non-ASCII text detected. You must set your Console\'s font to'
 
149
                               ' Lucida Console or Consolas or some other TrueType font to see this text', file=self.stream, end=' -- ')
 
150
                        from calibre.utils.filenames import ascii_text
 
151
                        print (ascii_text(t + text), file=self.stream, end='')
 
152
                        continue
 
153
                    if not ignore_errors:
 
154
                        raise ctypes.WinError(err)
 
155
 
 
156
_crt = None
 
157
def crt():
 
158
    # We use the C runtime bundled with the calibre windows build
 
159
    global _crt
 
160
    if _crt is None:
 
161
        import glob, ctypes
 
162
        d = os.path.join(os.path.dirname(sys.executable), '*.CRT', 'msvcr*.dll')
 
163
        _crt = ctypes.CDLL(glob.glob(d)[0])
 
164
    return _crt
 
165
 
111
166
class ColoredStream(Detect):
112
167
 
113
168
    def __init__(self, stream=None, fg=None, bg=None, bold=False):
118
173
 
119
174
    def __enter__(self):
120
175
        if not self.isatty:
121
 
            return
 
176
            return self
122
177
        if self.isansi:
123
178
            if self.bold:
124
179
                self.stream.write(ATTRIBUTES['bold'])
129
184
        elif self.set_console is not None:
130
185
            if self.wval != 0:
131
186
                self.set_console(self.file_handle, self.wval)
 
187
        return self
132
188
 
133
189
    def __exit__(self, *args, **kwargs):
134
190
        if not self.isatty:
180
236
            self.convert_ansi(*match.groups())
181
237
            cursor = end
182
238
        self.write_plain_text(text, cursor, len(text))
 
239
        self.stream.flush()
183
240
 
184
241
    def write_plain_text(self, text, start, end):
185
242
        if start < end:
186
 
            self.stream.write(text[start:end])
187
 
            self.stream.flush()
 
243
            text = text[start:end]
 
244
            if self.is_console and isinstance(text, bytes):
 
245
                try:
 
246
                    utext = text.decode(self.encoding)
 
247
                except ValueError:
 
248
                    pass
 
249
                else:
 
250
                    return self.write_unicode_text(utext)
 
251
            self.stream.write(text)
188
252
 
189
253
    def convert_ansi(self, paramstring, command):
190
254
        params = self.extract_params(paramstring)
198
262
        return tuple(split(paramstring))
199
263
 
200
264
    def call_win32(self, command, params):
201
 
        if command != b'm': return
 
265
        if command != b'm':
 
266
            return
202
267
        fg, bg, bold = self.last_state
203
268
 
204
269
        for param in params:
223
288
    from ctypes.wintypes import SHORT, WORD
224
289
 
225
290
    class COORD(Structure):
 
291
 
226
292
        """struct in wincon.h"""
227
293
        _fields_ = [
228
294
            ('X', SHORT),
229
295
            ('Y', SHORT),
230
296
        ]
231
297
 
232
 
    class  SMALL_RECT(Structure):
 
298
    class SMALL_RECT(Structure):
 
299
 
233
300
        """struct in wincon.h."""
234
301
        _fields_ = [
235
302
            ("Left", SHORT),
239
306
        ]
240
307
 
241
308
    class CONSOLE_SCREEN_BUFFER_INFO(Structure):
 
309
 
242
310
        """struct in wincon.h."""
243
311
        _fields_ = [
244
312
            ("dwSize", COORD),
281
349
    text = [colored(t, fg=t)+'. '+colored(t, fg=t, bold=True)+'.' for t in
282
350
            ('red', 'yellow', 'green', 'white', 'cyan', 'magenta', 'blue',)]
283
351
    s.write('\n'.join(text))
 
352
    u = u'\u041c\u0438\u0445\u0430\u0438\u043b fällen'
 
353
    print()
 
354
    s.write_unicode_text(u)
284
355
    print()
285
356