1"""Reimplementation of the standard extension module '_curses' using cffi."""
2
3import sys
4from functools import wraps
5
6from _curses_cffi import ffi, lib
7
8
9def _copy_to_globals(name):
10    globals()[name] = getattr(lib, name)
11
12
13def _setup():
14    for name in ['ERR', 'OK', 'KEY_MIN', 'KEY_MAX',
15                 'A_ATTRIBUTES', 'A_NORMAL', 'A_STANDOUT', 'A_UNDERLINE',
16                 'A_REVERSE', 'A_BLINK', 'A_DIM', 'A_BOLD', 'A_ALTCHARSET',
17                 'A_PROTECT', 'A_CHARTEXT', 'A_COLOR',
18                 'COLOR_BLACK', 'COLOR_RED', 'COLOR_GREEN', 'COLOR_YELLOW',
19                 'COLOR_BLUE', 'COLOR_MAGENTA', 'COLOR_CYAN', 'COLOR_WHITE',
20                 ]:
21        _copy_to_globals(name)
22
23    if not lib._m_NetBSD:
24        _copy_to_globals('A_INVIS')
25
26    for name in ['A_HORIZONTAL', 'A_LEFT', 'A_LOW', 'A_RIGHT', 'A_TOP',
27                 'A_VERTICAL',
28                 ]:
29        if hasattr(lib, name):
30            _copy_to_globals(name)
31
32    if lib._m_NCURSES_MOUSE_VERSION:
33        for name in ["BUTTON1_PRESSED", "BUTTON1_RELEASED", "BUTTON1_CLICKED",
34                     "BUTTON1_DOUBLE_CLICKED", "BUTTON1_TRIPLE_CLICKED",
35                     "BUTTON2_PRESSED", "BUTTON2_RELEASED", "BUTTON2_CLICKED",
36                     "BUTTON2_DOUBLE_CLICKED", "BUTTON2_TRIPLE_CLICKED",
37                     "BUTTON3_PRESSED", "BUTTON3_RELEASED", "BUTTON3_CLICKED",
38                     "BUTTON3_DOUBLE_CLICKED", "BUTTON3_TRIPLE_CLICKED",
39                     "BUTTON4_PRESSED", "BUTTON4_RELEASED", "BUTTON4_CLICKED",
40                     "BUTTON4_DOUBLE_CLICKED", "BUTTON4_TRIPLE_CLICKED",
41                     "BUTTON_SHIFT", "BUTTON_CTRL", "BUTTON_ALT",
42                     "ALL_MOUSE_EVENTS", "REPORT_MOUSE_POSITION",
43                     ]:
44            _copy_to_globals(name)
45
46    if not lib._m_NetBSD:
47        for key in range(lib.KEY_MIN, lib.KEY_MAX):
48            key_n = lib.keyname(key)
49            if key_n == ffi.NULL:
50                continue
51            key_n = ffi.string(key_n)
52            if key_n == b"UNKNOWN KEY":
53                continue
54            if not isinstance(key_n, str):   # python 3
55                key_n = key_n.decode()
56            key_n = key_n.replace('(', '').replace(')', '')
57            globals()[key_n] = key
58
59_setup()
60
61# Do we want this?
62# version = "2.2"
63# __version__ = "2.2"
64
65
66# ____________________________________________________________
67
68
69_initialised_setupterm = False
70_initialised = False
71_initialised_color = False
72
73
74def _ensure_initialised_setupterm():
75    if not _initialised_setupterm:
76        raise error("must call (at least) setupterm() first")
77
78
79def _ensure_initialised():
80    if not _initialised:
81        raise error("must call initscr() first")
82
83
84def _ensure_initialised_color():
85    if not _initialised and _initialised_color:
86        raise error("must call start_color() first")
87
88
89def _check_ERR(code, fname):
90    if code != lib.ERR:
91        return None
92    elif fname is None:
93        raise error("curses function returned ERR")
94    else:
95        raise error("%s() returned ERR" % (fname,))
96
97
98def _check_NULL(rval):
99    if rval == ffi.NULL:
100        raise error("curses function returned NULL")
101    return rval
102
103
104def _call_lib(method_name, *args):
105    return getattr(lib, method_name)(*args)
106
107
108def _call_lib_check_ERR(method_name, *args):
109    return _check_ERR(_call_lib(method_name, *args), method_name)
110
111
112def _mk_no_return(method_name):
113    def _execute():
114        _ensure_initialised()
115        return _call_lib_check_ERR(method_name)
116    _execute.__name__ = method_name
117    return _execute
118
119
120def _mk_flag_func(method_name):
121    # This is in the CPython implementation, but not documented anywhere.
122    # We have to support it, though, even if it make me sad.
123    def _execute(flag=True):
124        _ensure_initialised()
125        if flag:
126            return _call_lib_check_ERR(method_name)
127        else:
128            return _call_lib_check_ERR('no' + method_name)
129    _execute.__name__ = method_name
130    return _execute
131
132
133def _mk_return_val(method_name):
134    def _execute():
135        return _call_lib(method_name)
136    _execute.__name__ = method_name
137    return _execute
138
139
140def _mk_w_getyx(method_name):
141    def _execute(self):
142        y = _call_lib(method_name + 'y', self._win)
143        x = _call_lib(method_name + 'x', self._win)
144        return (y, x)
145    _execute.__name__ = method_name
146    return _execute
147
148
149def _mk_w_no_return(method_name):
150    def _execute(self, *args):
151        return _call_lib_check_ERR(method_name, self._win, *args)
152    _execute.__name__ = method_name
153    return _execute
154
155
156def _mk_w_return_val(method_name):
157    def _execute(self, *args):
158        return _call_lib(method_name, self._win, *args)
159    _execute.__name__ = method_name
160    return _execute
161
162
163def _chtype(ch):
164    return int(ffi.cast("chtype", ch))
165
166def _texttype(text):
167    if isinstance(text, str):
168        return text
169    elif isinstance(text, unicode):
170        return str(text)   # default encoding
171    else:
172        raise TypeError("str or unicode expected, got a '%s' object"
173                        % (type(text).__name__,))
174
175
176def _extract_yx(args):
177    if len(args) >= 2:
178        return (args[0], args[1], args[2:])
179    return (None, None, args)
180
181
182def _process_args(funcname, args, count, optcount, frontopt=0):
183    outargs = []
184    if frontopt:
185        if len(args) > count + optcount:
186            # We have the front optional args here.
187            outargs.extend(args[:frontopt])
188            args = args[frontopt:]
189        else:
190            # No front optional args, so make them None.
191            outargs.extend([None] * frontopt)
192    if (len(args) < count) or (len(args) > count + optcount):
193        raise error("%s requires %s to %s arguments" % (
194                funcname, count, count + optcount + frontopt))
195    outargs.extend(args)
196    return outargs
197
198
199def _argspec(count, optcount=0, frontopt=0):
200    def _argspec_deco(func):
201        @wraps(func)
202        def _wrapped(self, *args):
203            outargs = _process_args(
204                func.__name__, args, count, optcount, frontopt)
205            return func(self, *outargs)
206        return _wrapped
207    return _argspec_deco
208
209
210# ____________________________________________________________
211
212
213class error(Exception):
214    pass
215
216
217class Window(object):
218    def __init__(self, window):
219        self._win = window
220
221    def __del__(self):
222        if self._win != lib.stdscr:
223            lib.delwin(self._win)
224
225    untouchwin = _mk_w_no_return("untouchwin")
226    touchwin = _mk_w_no_return("touchwin")
227    redrawwin = _mk_w_no_return("redrawwin")
228    insertln = _mk_w_no_return("winsertln")
229    erase = _mk_w_no_return("werase")
230    deleteln = _mk_w_no_return("wdeleteln")
231
232    is_wintouched = _mk_w_return_val("is_wintouched")
233
234    syncdown = _mk_w_return_val("wsyncdown")
235    syncup = _mk_w_return_val("wsyncup")
236    standend = _mk_w_return_val("wstandend")
237    standout = _mk_w_return_val("wstandout")
238    cursyncup = _mk_w_return_val("wcursyncup")
239    clrtoeol = _mk_w_return_val("wclrtoeol")
240    clrtobot = _mk_w_return_val("wclrtobot")
241    clear = _mk_w_return_val("wclear")
242
243    idcok = _mk_w_no_return("idcok")
244    immedok = _mk_w_no_return("immedok")
245    timeout = _mk_w_no_return("wtimeout")
246
247    getyx = _mk_w_getyx("getcur")
248    getbegyx = _mk_w_getyx("getbeg")
249    getmaxyx = _mk_w_getyx("getmax")
250    getparyx = _mk_w_getyx("getpar")
251
252    clearok = _mk_w_no_return("clearok")
253    idlok = _mk_w_no_return("idlok")
254    leaveok = _mk_w_no_return("leaveok")
255    notimeout = _mk_w_no_return("notimeout")
256    scrollok = _mk_w_no_return("scrollok")
257    insdelln = _mk_w_no_return("winsdelln")
258    syncok = _mk_w_no_return("syncok")
259
260    mvwin = _mk_w_no_return("mvwin")
261    mvderwin = _mk_w_no_return("mvderwin")
262    move = _mk_w_no_return("wmove")
263
264    if not lib._m_STRICT_SYSV_CURSES:
265        resize = _mk_w_no_return("wresize")
266
267    if lib._m_NetBSD:
268        keypad = _mk_w_return_val("keypad")
269        nodelay = _mk_w_return_val("nodelay")
270    else:
271        keypad = _mk_w_no_return("keypad")
272        nodelay = _mk_w_no_return("nodelay")
273
274    @_argspec(1, 1, 2)
275    def addch(self, y, x, ch, attr=None):
276        if attr is None:
277            attr = lib.A_NORMAL
278        ch = _chtype(ch)
279
280        if y is not None:
281            code = lib.mvwaddch(self._win, y, x, ch | attr)
282        else:
283            code = lib.waddch(self._win, ch | attr)
284        return _check_ERR(code, "addch")
285
286    @_argspec(1, 1, 2)
287    def addstr(self, y, x, text, attr=None):
288        text = _texttype(text)
289        if attr is not None:
290            attr_old = lib.getattrs(self._win)
291            lib.wattrset(self._win, attr)
292        if y is not None:
293            code = lib.mvwaddstr(self._win, y, x, text)
294        else:
295            code = lib.waddstr(self._win, text)
296        if attr is not None:
297            lib.wattrset(self._win, attr_old)
298        return _check_ERR(code, "addstr")
299
300    @_argspec(2, 1, 2)
301    def addnstr(self, y, x, text, n, attr=None):
302        text = _texttype(text)
303        if attr is not None:
304            attr_old = lib.getattrs(self._win)
305            lib.wattrset(self._win, attr)
306        if y is not None:
307            code = lib.mvwaddnstr(self._win, y, x, text, n)
308        else:
309            code = lib.waddnstr(self._win, text, n)
310        if attr is not None:
311            lib.wattrset(self._win, attr_old)
312        return _check_ERR(code, "addnstr")
313
314    def bkgd(self, ch, attr=None):
315        if attr is None:
316            attr = lib.A_NORMAL
317        return _check_ERR(lib.wbkgd(self._win, _chtype(ch) | attr), "bkgd")
318
319    attroff = _mk_w_no_return("wattroff")
320    attron = _mk_w_no_return("wattron")
321    attrset = _mk_w_no_return("wattrset")
322
323    def bkgdset(self, ch, attr=None):
324        if attr is None:
325            attr = lib.A_NORMAL
326        lib.wbkgdset(self._win, _chtype(ch) | attr)
327        return None
328
329    def border(self, ls=0, rs=0, ts=0, bs=0, tl=0, tr=0, bl=0, br=0):
330        lib.wborder(self._win,
331                    _chtype(ls), _chtype(rs), _chtype(ts), _chtype(bs),
332                    _chtype(tl), _chtype(tr), _chtype(bl), _chtype(br))
333        return None
334
335    def box(self, vertint=0, horint=0):
336        lib.box(self._win, vertint, horint)
337        return None
338
339    @_argspec(1, 1, 2)
340    def chgat(self, y, x, num, attr=None):
341        # These optional args are in a weird order.
342        if attr is None:
343            attr = num
344            num = -1
345
346        color = ((attr >> 8) & 0xff)
347        attr = attr - (color << 8)
348
349        if y is not None:
350            code = lib.mvwchgat(self._win, y, x, num, attr, color, ffi.NULL)
351            lib.touchline(self._win, y, 1)
352        else:
353            yy, _ = self.getyx()
354            code = lib.wchgat(self._win, num, attr, color, ffi.NULL)
355            lib.touchline(self._win, yy, 1)
356        return _check_ERR(code, "chgat")
357
358    def delch(self, *args):
359        if len(args) == 0:
360            code = lib.wdelch(self._win)
361        elif len(args) == 2:
362            code = lib.mvwdelch(self._win, *args)
363        else:
364            raise error("delch requires 0 or 2 arguments")
365        return _check_ERR(code, "[mv]wdelch")
366
367    def derwin(self, *args):
368        nlines = 0
369        ncols = 0
370        if len(args) == 2:
371            begin_y, begin_x = args
372        elif len(args) == 4:
373            nlines, ncols, begin_y, begin_x = args
374        else:
375            raise error("derwin requires 2 or 4 arguments")
376
377        win = lib.derwin(self._win, nlines, ncols, begin_y, begin_x)
378        return Window(_check_NULL(win))
379
380    def echochar(self, ch, attr=None):
381        if attr is None:
382            attr = lib.A_NORMAL
383        ch = _chtype(ch)
384
385        if lib._m_ispad(self._win):
386            code = lib.pechochar(self._win, ch | attr)
387        else:
388            code = lib.wechochar(self._win, ch | attr)
389        return _check_ERR(code, "echochar")
390
391    if lib._m_NCURSES_MOUSE_VERSION:
392        enclose = _mk_w_return_val("wenclose")
393
394    getbkgd = _mk_w_return_val("getbkgd")
395
396    def getch(self, *args):
397        if len(args) == 0:
398            val = lib.wgetch(self._win)
399        elif len(args) == 2:
400            val = lib.mvwgetch(self._win, *args)
401        else:
402            raise error("getch requires 0 or 2 arguments")
403        return val
404
405    def getkey(self, *args):
406        if len(args) == 0:
407            val = lib.wgetch(self._win)
408        elif len(args) == 2:
409            val = lib.mvwgetch(self._win, *args)
410        else:
411            raise error("getkey requires 0 or 2 arguments")
412
413        if val == lib.ERR:
414            raise error("no input")
415        elif val <= 255:
416            return chr(val)
417        else:
418            # XXX: The following line is different if `__NetBSD__` is defined.
419            val = lib.keyname(val)
420            if val == ffi.NULL:
421                return ""
422            return ffi.string(val)
423
424    @_argspec(0, 1, 2)
425    def getstr(self, y, x, n=1023):
426        n = min(n, 1023)
427        buf = ffi.new("char[1024]")  # /* This should be big enough.. I hope */
428
429        if y is None:
430            val = lib.wgetnstr(self._win, buf, n)
431        else:
432            val = lib.mvwgetnstr(self._win, y, x, buf, n)
433
434        if val == lib.ERR:
435            return ""
436        return ffi.string(buf)
437
438    @_argspec(2, 1, 2)
439    def hline(self, y, x, ch, n, attr=None):
440        ch = _chtype(ch)
441        if attr is None:
442            attr = lib.A_NORMAL
443        if y is not None:
444            _check_ERR(lib.wmove(self._win, y, x), "wmove")
445        return _check_ERR(lib.whline(self._win, ch | attr, n), "hline")
446
447    @_argspec(1, 1, 2)
448    def insch(self, y, x, ch, attr=None):
449        ch = _chtype(ch)
450        if attr is None:
451            attr = lib.A_NORMAL
452        if y is not None:
453            code = lib.mvwinsch(self._win, y, x, ch | attr)
454        else:
455            code = lib.winsch(self._win, ch | attr)
456        return _check_ERR(code, "insch")
457
458    def inch(self, *args):
459        if len(args) == 0:
460            return lib.winch(self._win)
461        elif len(args) == 2:
462            return lib.mvwinch(self._win, *args)
463        else:
464            raise error("inch requires 0 or 2 arguments")
465
466    @_argspec(0, 1, 2)
467    def instr(self, y, x, n=1023):
468        n = min(n, 1023)
469        buf = ffi.new("char[1024]")  # /* This should be big enough.. I hope */
470        if y is None:
471            code = lib.winnstr(self._win, buf, n)
472        else:
473            code = lib.mvwinnstr(self._win, y, x, buf, n)
474
475        if code == lib.ERR:
476            return ""
477        return ffi.string(buf)
478
479    @_argspec(1, 1, 2)
480    def insstr(self, y, x, text, attr=None):
481        text = _texttype(text)
482        if attr is not None:
483            attr_old = lib.getattrs(self._win)
484            lib.wattrset(self._win, attr)
485        if y is not None:
486            code = lib.mvwinsstr(self._win, y, x, text)
487        else:
488            code = lib.winsstr(self._win, text)
489        if attr is not None:
490            lib.wattrset(self._win, attr_old)
491        return _check_ERR(code, "insstr")
492
493    @_argspec(2, 1, 2)
494    def insnstr(self, y, x, text, n, attr=None):
495        text = _texttype(text)
496        if attr is not None:
497            attr_old = lib.getattrs(self._win)
498            lib.wattrset(self._win, attr)
499        if y is not None:
500            code = lib.mvwinsnstr(self._win, y, x, text, n)
501        else:
502            code = lib.winsnstr(self._win, text, n)
503        if attr is not None:
504            lib.wattrset(self._win, attr_old)
505        return _check_ERR(code, "insnstr")
506
507    def is_linetouched(self, line):
508        code = lib.is_linetouched(self._win, line)
509        if code == lib.ERR:
510            raise error("is_linetouched: line number outside of boundaries")
511        if code == lib.FALSE:
512            return False
513        return True
514
515    def noutrefresh(self, *args):
516        if lib._m_ispad(self._win):
517            if len(args) != 6:
518                raise error(
519                    "noutrefresh() called for a pad requires 6 arguments")
520            return _check_ERR(lib.pnoutrefresh(self._win, *args),
521                              "pnoutrefresh")
522        else:
523            # XXX: Better args check here? We need zero args.
524            return _check_ERR(lib.wnoutrefresh(self._win, *args),
525                              "wnoutrefresh")
526
527    nooutrefresh = noutrefresh  # "to be removed in 2.3", but in 2.7, 3.x.
528
529    def _copywin(self, dstwin, overlay,
530                 sminr, sminc, dminr, dminc, dmaxr, dmaxc):
531        return _check_ERR(lib.copywin(self._win, dstwin._win,
532                                      sminr, sminc, dminr, dminc, dmaxr, dmaxc,
533                                      overlay), "copywin")
534
535    def overlay(self, dstwin, *args):
536        if len(args) == 6:
537            return self._copywin(dstwin, True, *args)
538        elif len(args) == 0:
539            return _check_ERR(lib.overlay(self._win, dstwin._win), "overlay")
540        else:
541            raise error("overlay requires one or seven arguments")
542
543    def overwrite(self, dstwin, *args):
544        if len(args) == 6:
545            return self._copywin(dstwin, False, *args)
546        elif len(args) == 0:
547            return _check_ERR(lib.overwrite(self._win, dstwin._win),
548                              "overwrite")
549        else:
550            raise error("overwrite requires one or seven arguments")
551
552    def putwin(self, filep):
553        # filestar = ffi.new("FILE *", filep)
554        return _check_ERR(lib.putwin(self._win, filep), "putwin")
555
556    def redrawln(self, beg, num):
557        return _check_ERR(lib.wredrawln(self._win, beg, num), "redrawln")
558
559    def refresh(self, *args):
560        if lib._m_ispad(self._win):
561            if len(args) != 6:
562                raise error(
563                    "noutrefresh() called for a pad requires 6 arguments")
564            return _check_ERR(lib.prefresh(self._win, *args), "prefresh")
565        else:
566            # XXX: Better args check here? We need zero args.
567            return _check_ERR(lib.wrefresh(self._win, *args), "wrefresh")
568
569    def setscrreg(self, y, x):
570        return _check_ERR(lib.wsetscrreg(self._win, y, x), "wsetscrreg")
571
572    def subwin(self, *args):
573        nlines = 0
574        ncols = 0
575        if len(args) == 2:
576            begin_y, begin_x = args
577        elif len(args) == 4:
578            nlines, ncols, begin_y, begin_x = args
579        else:
580            raise error("subwin requires 2 or 4 arguments")
581
582        if lib._m_ispad(self._win):
583            win = lib.subpad(self._win, nlines, ncols, begin_y, begin_x)
584        else:
585            win = lib.subwin(self._win, nlines, ncols, begin_y, begin_x)
586        return Window(_check_NULL(win))
587
588    def scroll(self, nlines=None):
589        if nlines is None:
590            return _check_ERR(lib.scroll(self._win), "scroll")
591        else:
592            return _check_ERR(lib.wscrl(self._win, nlines), "scroll")
593
594    def touchline(self, st, cnt, val=None):
595        if val is None:
596            return _check_ERR(lib.touchline(self._win, st, cnt), "touchline")
597        else:
598            return _check_ERR(lib.wtouchln(self._win, st, cnt, val),
599                              "touchline")
600
601    @_argspec(2, 1, 2)
602    def vline(self, y, x, ch, n, attr=None):
603        ch = _chtype(ch)
604        if attr is None:
605            attr = lib.A_NORMAL
606        if y is not None:
607            _check_ERR(lib.wmove(self._win, y, x), "wmove")
608        return _check_ERR(lib.wvline(self._win, ch | attr, n), "vline")
609
610
611beep = _mk_no_return("beep")
612def_prog_mode = _mk_no_return("def_prog_mode")
613def_shell_mode = _mk_no_return("def_shell_mode")
614doupdate = _mk_no_return("doupdate")
615endwin = _mk_no_return("endwin")
616flash = _mk_no_return("flash")
617nocbreak = _mk_no_return("nocbreak")
618noecho = _mk_no_return("noecho")
619nonl = _mk_no_return("nonl")
620noraw = _mk_no_return("noraw")
621reset_prog_mode = _mk_no_return("reset_prog_mode")
622reset_shell_mode = _mk_no_return("reset_shell_mode")
623resetty = _mk_no_return("resetty")
624savetty = _mk_no_return("savetty")
625
626cbreak = _mk_flag_func("cbreak")
627echo = _mk_flag_func("echo")
628nl = _mk_flag_func("nl")
629raw = _mk_flag_func("raw")
630
631baudrate = _mk_return_val("baudrate")
632termattrs = _mk_return_val("termattrs")
633
634termname = _mk_return_val("termname")
635longname = _mk_return_val("longname")
636
637can_change_color = _mk_return_val("can_change_color")
638has_colors = _mk_return_val("has_colors")
639has_ic = _mk_return_val("has_ic")
640has_il = _mk_return_val("has_il")
641isendwin = _mk_return_val("isendwin")
642flushinp = _mk_return_val("flushinp")
643noqiflush = _mk_return_val("noqiflush")
644
645
646def filter():
647    lib.filter()
648    return None
649
650
651def color_content(color):
652    _ensure_initialised_color()
653    r, g, b = ffi.new("short *"), ffi.new("short *"), ffi.new("short *")
654    if lib.color_content(color, r, g, b) == lib.ERR:
655        raise error("Argument 1 was out of range. Check value of COLORS.")
656    return (r[0], g[0], b[0])
657
658
659def color_pair(n):
660    _ensure_initialised_color()
661    return (n << 8)
662
663
664def curs_set(vis):
665    _ensure_initialised()
666    val = lib.curs_set(vis)
667    _check_ERR(val, "curs_set")
668    return val
669
670
671def delay_output(ms):
672    _ensure_initialised()
673    return _check_ERR(lib.delay_output(ms), "delay_output")
674
675
676def erasechar():
677    _ensure_initialised()
678    return lib.erasechar()
679
680
681def getsyx():
682    _ensure_initialised()
683    yx = ffi.new("int[2]")
684    lib._m_getsyx(yx)
685    return (yx[0], yx[1])
686
687
688if lib._m_NCURSES_MOUSE_VERSION:
689
690    def getmouse():
691        _ensure_initialised()
692        mevent = ffi.new("MEVENT *")
693        _check_ERR(lib.getmouse(mevent), "getmouse")
694        return (mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate)
695
696    def ungetmouse(id, x, y, z, bstate):
697        _ensure_initialised()
698        mevent = ffi.new("MEVENT *")
699        mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate = (
700            id, x, y, z, bstate)
701        return _check_ERR(lib.ungetmouse(mevent), "ungetmouse")
702
703
704def getwin(filep):
705    return Window(_check_NULL(lib.getwin(filep)))
706
707
708def halfdelay(tenths):
709    _ensure_initialised()
710    return _check_ERR(lib.halfdelay(tenths), "halfdelay")
711
712
713if not lib._m_STRICT_SYSV_CURSES:
714    def has_key(ch):
715        _ensure_initialised()
716        return lib.has_key(ch)
717
718
719def init_color(color, r, g, b):
720    _ensure_initialised_color()
721    return _check_ERR(lib.init_color(color, r, g, b), "init_color")
722
723
724def init_pair(pair, f, b):
725    _ensure_initialised_color()
726    return _check_ERR(lib.init_pair(pair, f, b), "init_pair")
727
728
729def _mk_acs(name, ichar):
730    if len(ichar) == 1:
731        globals()[name] = lib.acs_map[ord(ichar)]
732    else:
733        globals()[name] = globals()[ichar]
734
735
736def _map_acs():
737    _mk_acs("ACS_ULCORNER", 'l')
738    _mk_acs("ACS_LLCORNER", 'm')
739    _mk_acs("ACS_URCORNER", 'k')
740    _mk_acs("ACS_LRCORNER", 'j')
741    _mk_acs("ACS_LTEE", 't')
742    _mk_acs("ACS_RTEE", 'u')
743    _mk_acs("ACS_BTEE", 'v')
744    _mk_acs("ACS_TTEE", 'w')
745    _mk_acs("ACS_HLINE", 'q')
746    _mk_acs("ACS_VLINE", 'x')
747    _mk_acs("ACS_PLUS", 'n')
748    _mk_acs("ACS_S1", 'o')
749    _mk_acs("ACS_S9", 's')
750    _mk_acs("ACS_DIAMOND", '`')
751    _mk_acs("ACS_CKBOARD", 'a')
752    _mk_acs("ACS_DEGREE", 'f')
753    _mk_acs("ACS_PLMINUS", 'g')
754    _mk_acs("ACS_BULLET", '~')
755    _mk_acs("ACS_LARROW", ',')
756    _mk_acs("ACS_RARROW", '+')
757    _mk_acs("ACS_DARROW", '.')
758    _mk_acs("ACS_UARROW", '-')
759    _mk_acs("ACS_BOARD", 'h')
760    _mk_acs("ACS_LANTERN", 'i')
761    _mk_acs("ACS_BLOCK", '0')
762    _mk_acs("ACS_S3", 'p')
763    _mk_acs("ACS_S7", 'r')
764    _mk_acs("ACS_LEQUAL", 'y')
765    _mk_acs("ACS_GEQUAL", 'z')
766    _mk_acs("ACS_PI", '{')
767    _mk_acs("ACS_NEQUAL", '|')
768    _mk_acs("ACS_STERLING", '}')
769    _mk_acs("ACS_BSSB", "ACS_ULCORNER")
770    _mk_acs("ACS_SSBB", "ACS_LLCORNER")
771    _mk_acs("ACS_BBSS", "ACS_URCORNER")
772    _mk_acs("ACS_SBBS", "ACS_LRCORNER")
773    _mk_acs("ACS_SBSS", "ACS_RTEE")
774    _mk_acs("ACS_SSSB", "ACS_LTEE")
775    _mk_acs("ACS_SSBS", "ACS_BTEE")
776    _mk_acs("ACS_BSSS", "ACS_TTEE")
777    _mk_acs("ACS_BSBS", "ACS_HLINE")
778    _mk_acs("ACS_SBSB", "ACS_VLINE")
779    _mk_acs("ACS_SSSS", "ACS_PLUS")
780
781
782def initscr():
783    if _initialised:
784        lib.wrefresh(lib.stdscr)
785        return Window(lib.stdscr)
786
787    win = _check_NULL(lib.initscr())
788    globals()['_initialised_setupterm'] = True
789    globals()['_initialised'] = True
790
791    _map_acs()
792
793    globals()["LINES"] = lib.LINES
794    globals()["COLS"] = lib.COLS
795
796    return Window(win)
797
798
799def setupterm(term=None, fd=-1):
800    if fd == -1:
801        # XXX: Check for missing stdout here?
802        fd = sys.stdout.fileno()
803
804    if _initialised_setupterm:
805        return None
806
807    if term is None:
808        term = ffi.NULL
809    err = ffi.new("int *")
810    if lib.setupterm(term, fd, err) == lib.ERR:
811        err = err[0]
812        if err == 0:
813            raise error("setupterm: could not find terminal")
814        elif err == -1:
815            raise error("setupterm: could not find terminfo database")
816        else:
817            raise error("setupterm: unknown error")
818
819    globals()["_initialised_setupterm"] = True
820    return None
821
822
823def intrflush(ch):
824    _ensure_initialised()
825    return _check_ERR(lib.intrflush(ffi.NULL, ch), "intrflush")
826
827
828# XXX: #ifdef HAVE_CURSES_IS_TERM_RESIZED
829def is_term_resized(lines, columns):
830    _ensure_initialised()
831    return lib.is_term_resized(lines, columns)
832
833
834if not lib._m_NetBSD:
835    def keyname(ch):
836        _ensure_initialised()
837        if ch < 0:
838            raise error("invalid key number")
839        knp = lib.keyname(ch)
840        if knp == ffi.NULL:
841            return ""
842        return ffi.string(knp)
843
844
845def killchar():
846    return lib.killchar()
847
848
849def meta(ch):
850    return _check_ERR(lib.meta(lib.stdscr, ch), "meta")
851
852
853if lib._m_NCURSES_MOUSE_VERSION:
854
855    def mouseinterval(interval):
856        _ensure_initialised()
857        return _check_ERR(lib.mouseinterval(interval), "mouseinterval")
858
859    def mousemask(newmask):
860        _ensure_initialised()
861        oldmask = ffi.new("mmask_t *")
862        availmask = lib.mousemask(newmask, oldmask)
863        return (availmask, oldmask)
864
865
866def napms(ms):
867    _ensure_initialised()
868    return lib.napms(ms)
869
870
871def newpad(nlines, ncols):
872    _ensure_initialised()
873    return Window(_check_NULL(lib.newpad(nlines, ncols)))
874
875
876def newwin(nlines, ncols, begin_y=None, begin_x=None):
877    _ensure_initialised()
878    if begin_x is None:
879        if begin_y is not None:
880            raise error("newwin requires 2 or 4 arguments")
881        begin_y = begin_x = 0
882
883    return Window(_check_NULL(lib.newwin(nlines, ncols, begin_y, begin_x)))
884
885
886def pair_content(pair):
887    _ensure_initialised_color()
888    f = ffi.new("short *")
889    b = ffi.new("short *")
890    if lib.pair_content(pair, f, b) == lib.ERR:
891        raise error("Argument 1 was out of range. (1..COLOR_PAIRS-1)")
892    return (f, b)
893
894
895def pair_number(pairvalue):
896    _ensure_initialised_color()
897    return (pairvalue & lib.A_COLOR) >> 8
898
899
900def putp(text):
901    text = _texttype(text)
902    return _check_ERR(lib.putp(text), "putp")
903
904
905def qiflush(flag=True):
906    _ensure_initialised()
907    if flag:
908        lib.qiflush()
909    else:
910        lib.noqiflush()
911    return None
912
913
914# XXX: Do something about the following?
915# /* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES
916#  * and _curses.COLS */
917# #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM)
918# static int
919# update_lines_cols(void)
920# {
921#     PyObject *o;
922#     PyObject *m = PyImport_ImportModuleNoBlock("curses");
923
924#     if (!m)
925#         return 0;
926
927#     o = PyInt_FromLong(LINES);
928#     if (!o) {
929#         Py_DECREF(m);
930#         return 0;
931#     }
932#     if (PyObject_SetAttrString(m, "LINES", o)) {
933#         Py_DECREF(m);
934#         Py_DECREF(o);
935#         return 0;
936#     }
937#     if (PyDict_SetItemString(ModDict, "LINES", o)) {
938#         Py_DECREF(m);
939#         Py_DECREF(o);
940#         return 0;
941#     }
942#     Py_DECREF(o);
943#     o = PyInt_FromLong(COLS);
944#     if (!o) {
945#         Py_DECREF(m);
946#         return 0;
947#     }
948#     if (PyObject_SetAttrString(m, "COLS", o)) {
949#         Py_DECREF(m);
950#         Py_DECREF(o);
951#         return 0;
952#     }
953#     if (PyDict_SetItemString(ModDict, "COLS", o)) {
954#         Py_DECREF(m);
955#         Py_DECREF(o);
956#         return 0;
957#     }
958#     Py_DECREF(o);
959#     Py_DECREF(m);
960#     return 1;
961# }
962# #endif
963
964# #ifdef HAVE_CURSES_RESIZETERM
965# static PyObject *
966# PyCurses_ResizeTerm(PyObject *self, PyObject *args)
967# {
968#     int lines;
969#     int columns;
970#     PyObject *result;
971
972#     PyCursesInitialised;
973
974#     if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns))
975#         return NULL;
976
977#     result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm");
978#     if (!result)
979#         return NULL;
980#     if (!update_lines_cols())
981#         return NULL;
982#     return result;
983# }
984
985# #endif
986
987# #ifdef HAVE_CURSES_RESIZE_TERM
988# static PyObject *
989# PyCurses_Resize_Term(PyObject *self, PyObject *args)
990# {
991#     int lines;
992#     int columns;
993
994#     PyObject *result;
995
996#     PyCursesInitialised;
997
998#     if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns))
999#         return NULL;
1000
1001#     result = PyCursesCheckERR(resize_term(lines, columns), "resize_term");
1002#     if (!result)
1003#         return NULL;
1004#     if (!update_lines_cols())
1005#         return NULL;
1006#     return result;
1007# }
1008# #endif /* HAVE_CURSES_RESIZE_TERM */
1009
1010
1011def setsyx(y, x):
1012    _ensure_initialised()
1013    lib.setsyx(y, x)
1014    return None
1015
1016
1017def start_color():
1018    _check_ERR(lib.start_color(), "start_color")
1019    globals()["COLORS"] = lib.COLORS
1020    globals()["COLOR_PAIRS"] = lib.COLOR_PAIRS
1021    globals()["_initialised_color"] = True
1022    return None
1023
1024
1025def tigetflag(capname):
1026    _ensure_initialised_setupterm()
1027    return lib.tigetflag(capname)
1028
1029
1030def tigetnum(capname):
1031    _ensure_initialised_setupterm()
1032    return lib.tigetnum(capname)
1033
1034
1035def tigetstr(capname):
1036    _ensure_initialised_setupterm()
1037    val = lib.tigetstr(capname)
1038    if int(ffi.cast("intptr_t", val)) in (0, -1):
1039        return None
1040    return ffi.string(val)
1041
1042
1043def tparm(fmt, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0):
1044    args = [ffi.cast("int", i) for i in (i1, i2, i3, i4, i5, i6, i7, i8, i9)]
1045    result = lib.tparm(fmt, *args)
1046    if result == ffi.NULL:
1047        raise error("tparm() returned NULL")
1048    return ffi.string(result)
1049
1050
1051def typeahead(fd):
1052    _ensure_initialised()
1053    return _check_ERR(lib.typeahead(fd), "typeahead")
1054
1055
1056def unctrl(ch):
1057    _ensure_initialised()
1058    return lib.unctrl(_chtype(ch))
1059
1060
1061def ungetch(ch):
1062    _ensure_initialised()
1063    return _check_ERR(lib.ungetch(_chtype(ch)), "ungetch")
1064
1065
1066def use_env(flag):
1067    lib.use_env(flag)
1068    return None
1069
1070
1071if not lib._m_STRICT_SYSV_CURSES:
1072
1073    def use_default_colors():
1074        _ensure_initialised_color()
1075        return _check_ERR(lib.use_default_colors(), "use_default_colors")
1076