diff options
Diffstat (limited to 'demo')
36 files changed, 3001 insertions, 0 deletions
diff --git a/demo/_curses.py b/demo/_curses.py new file mode 100644 index 0000000..46443c2 --- /dev/null +++ b/demo/_curses.py @@ -0,0 +1,1075 @@ +"""Reimplementation of the standard extension module '_curses' using cffi.""" + +import sys +from functools import wraps + +from _curses_cffi import ffi, lib + + +def _copy_to_globals(name): + globals()[name] = getattr(lib, name) + + +def _setup(): + for name in ['ERR', 'OK', 'KEY_MIN', 'KEY_MAX', + 'A_ATTRIBUTES', 'A_NORMAL', 'A_STANDOUT', 'A_UNDERLINE', + 'A_REVERSE', 'A_BLINK', 'A_DIM', 'A_BOLD', 'A_ALTCHARSET', + 'A_PROTECT', 'A_CHARTEXT', 'A_COLOR', + 'COLOR_BLACK', 'COLOR_RED', 'COLOR_GREEN', 'COLOR_YELLOW', + 'COLOR_BLUE', 'COLOR_MAGENTA', 'COLOR_CYAN', 'COLOR_WHITE', + ]: + _copy_to_globals(name) + + if not lib._m_NetBSD: + _copy_to_globals('A_INVIS') + + for name in ['A_HORIZONTAL', 'A_LEFT', 'A_LOW', 'A_RIGHT', 'A_TOP', + 'A_VERTICAL', + ]: + if hasattr(lib, name): + _copy_to_globals(name) + + if lib._m_NCURSES_MOUSE_VERSION: + for name in ["BUTTON1_PRESSED", "BUTTON1_RELEASED", "BUTTON1_CLICKED", + "BUTTON1_DOUBLE_CLICKED", "BUTTON1_TRIPLE_CLICKED", + "BUTTON2_PRESSED", "BUTTON2_RELEASED", "BUTTON2_CLICKED", + "BUTTON2_DOUBLE_CLICKED", "BUTTON2_TRIPLE_CLICKED", + "BUTTON3_PRESSED", "BUTTON3_RELEASED", "BUTTON3_CLICKED", + "BUTTON3_DOUBLE_CLICKED", "BUTTON3_TRIPLE_CLICKED", + "BUTTON4_PRESSED", "BUTTON4_RELEASED", "BUTTON4_CLICKED", + "BUTTON4_DOUBLE_CLICKED", "BUTTON4_TRIPLE_CLICKED", + "BUTTON_SHIFT", "BUTTON_CTRL", "BUTTON_ALT", + "ALL_MOUSE_EVENTS", "REPORT_MOUSE_POSITION", + ]: + _copy_to_globals(name) + + if not lib._m_NetBSD: + for key in range(lib.KEY_MIN, lib.KEY_MAX): + key_n = lib.keyname(key) + if key_n == ffi.NULL: + continue + key_n = ffi.string(key_n) + if key_n == b"UNKNOWN KEY": + continue + if not isinstance(key_n, str): # python 3 + key_n = key_n.decode() + key_n = key_n.replace('(', '').replace(')', '') + globals()[key_n] = key + +_setup() + +# Do we want this? +# version = "2.2" +# __version__ = "2.2" + + +# ____________________________________________________________ + + +_initialised_setupterm = False +_initialised = False +_initialised_color = False + + +def _ensure_initialised_setupterm(): + if not _initialised_setupterm: + raise error("must call (at least) setupterm() first") + + +def _ensure_initialised(): + if not _initialised: + raise error("must call initscr() first") + + +def _ensure_initialised_color(): + if not _initialised and _initialised_color: + raise error("must call start_color() first") + + +def _check_ERR(code, fname): + if code != lib.ERR: + return None + elif fname is None: + raise error("curses function returned ERR") + else: + raise error("%s() returned ERR" % (fname,)) + + +def _check_NULL(rval): + if rval == ffi.NULL: + raise error("curses function returned NULL") + return rval + + +def _call_lib(method_name, *args): + return getattr(lib, method_name)(*args) + + +def _call_lib_check_ERR(method_name, *args): + return _check_ERR(_call_lib(method_name, *args), method_name) + + +def _mk_no_return(method_name): + def _execute(): + _ensure_initialised() + return _call_lib_check_ERR(method_name) + _execute.__name__ = method_name + return _execute + + +def _mk_flag_func(method_name): + # This is in the CPython implementation, but not documented anywhere. + # We have to support it, though, even if it make me sad. + def _execute(flag=True): + _ensure_initialised() + if flag: + return _call_lib_check_ERR(method_name) + else: + return _call_lib_check_ERR('no' + method_name) + _execute.__name__ = method_name + return _execute + + +def _mk_return_val(method_name): + def _execute(): + return _call_lib(method_name) + _execute.__name__ = method_name + return _execute + + +def _mk_w_getyx(method_name): + def _execute(self): + y = _call_lib(method_name + 'y', self._win) + x = _call_lib(method_name + 'x', self._win) + return (y, x) + _execute.__name__ = method_name + return _execute + + +def _mk_w_no_return(method_name): + def _execute(self, *args): + return _call_lib_check_ERR(method_name, self._win, *args) + _execute.__name__ = method_name + return _execute + + +def _mk_w_return_val(method_name): + def _execute(self, *args): + return _call_lib(method_name, self._win, *args) + _execute.__name__ = method_name + return _execute + + +def _chtype(ch): + return int(ffi.cast("chtype", ch)) + +def _texttype(text): + if isinstance(text, str): + return text + elif isinstance(text, unicode): + return str(text) # default encoding + else: + raise TypeError("str or unicode expected, got a '%s' object" + % (type(text).__name__,)) + + +def _extract_yx(args): + if len(args) >= 2: + return (args[0], args[1], args[2:]) + return (None, None, args) + + +def _process_args(funcname, args, count, optcount, frontopt=0): + outargs = [] + if frontopt: + if len(args) > count + optcount: + # We have the front optional args here. + outargs.extend(args[:frontopt]) + args = args[frontopt:] + else: + # No front optional args, so make them None. + outargs.extend([None] * frontopt) + if (len(args) < count) or (len(args) > count + optcount): + raise error("%s requires %s to %s arguments" % ( + funcname, count, count + optcount + frontopt)) + outargs.extend(args) + return outargs + + +def _argspec(count, optcount=0, frontopt=0): + def _argspec_deco(func): + @wraps(func) + def _wrapped(self, *args): + outargs = _process_args( + func.__name__, args, count, optcount, frontopt) + return func(self, *outargs) + return _wrapped + return _argspec_deco + + +# ____________________________________________________________ + + +class error(Exception): + pass + + +class Window(object): + def __init__(self, window): + self._win = window + + def __del__(self): + if self._win != lib.stdscr: + lib.delwin(self._win) + + untouchwin = _mk_w_no_return("untouchwin") + touchwin = _mk_w_no_return("touchwin") + redrawwin = _mk_w_no_return("redrawwin") + insertln = _mk_w_no_return("winsertln") + erase = _mk_w_no_return("werase") + deleteln = _mk_w_no_return("wdeleteln") + + is_wintouched = _mk_w_return_val("is_wintouched") + + syncdown = _mk_w_return_val("wsyncdown") + syncup = _mk_w_return_val("wsyncup") + standend = _mk_w_return_val("wstandend") + standout = _mk_w_return_val("wstandout") + cursyncup = _mk_w_return_val("wcursyncup") + clrtoeol = _mk_w_return_val("wclrtoeol") + clrtobot = _mk_w_return_val("wclrtobot") + clear = _mk_w_return_val("wclear") + + idcok = _mk_w_no_return("idcok") + immedok = _mk_w_no_return("immedok") + timeout = _mk_w_no_return("wtimeout") + + getyx = _mk_w_getyx("getcur") + getbegyx = _mk_w_getyx("getbeg") + getmaxyx = _mk_w_getyx("getmax") + getparyx = _mk_w_getyx("getpar") + + clearok = _mk_w_no_return("clearok") + idlok = _mk_w_no_return("idlok") + leaveok = _mk_w_no_return("leaveok") + notimeout = _mk_w_no_return("notimeout") + scrollok = _mk_w_no_return("scrollok") + insdelln = _mk_w_no_return("winsdelln") + syncok = _mk_w_no_return("syncok") + + mvwin = _mk_w_no_return("mvwin") + mvderwin = _mk_w_no_return("mvderwin") + move = _mk_w_no_return("wmove") + + if not lib._m_STRICT_SYSV_CURSES: + resize = _mk_w_no_return("wresize") + + if lib._m_NetBSD: + keypad = _mk_w_return_val("keypad") + nodelay = _mk_w_return_val("nodelay") + else: + keypad = _mk_w_no_return("keypad") + nodelay = _mk_w_no_return("nodelay") + + @_argspec(1, 1, 2) + def addch(self, y, x, ch, attr=None): + if attr is None: + attr = lib.A_NORMAL + ch = _chtype(ch) + + if y is not None: + code = lib.mvwaddch(self._win, y, x, ch | attr) + else: + code = lib.waddch(self._win, ch | attr) + return _check_ERR(code, "addch") + + @_argspec(1, 1, 2) + def addstr(self, y, x, text, attr=None): + text = _texttype(text) + if attr is not None: + attr_old = lib.getattrs(self._win) + lib.wattrset(self._win, attr) + if y is not None: + code = lib.mvwaddstr(self._win, y, x, text) + else: + code = lib.waddstr(self._win, text) + if attr is not None: + lib.wattrset(self._win, attr_old) + return _check_ERR(code, "addstr") + + @_argspec(2, 1, 2) + def addnstr(self, y, x, text, n, attr=None): + text = _texttype(text) + if attr is not None: + attr_old = lib.getattrs(self._win) + lib.wattrset(self._win, attr) + if y is not None: + code = lib.mvwaddnstr(self._win, y, x, text, n) + else: + code = lib.waddnstr(self._win, text, n) + if attr is not None: + lib.wattrset(self._win, attr_old) + return _check_ERR(code, "addnstr") + + def bkgd(self, ch, attr=None): + if attr is None: + attr = lib.A_NORMAL + return _check_ERR(lib.wbkgd(self._win, _chtype(ch) | attr), "bkgd") + + attroff = _mk_w_no_return("wattroff") + attron = _mk_w_no_return("wattron") + attrset = _mk_w_no_return("wattrset") + + def bkgdset(self, ch, attr=None): + if attr is None: + attr = lib.A_NORMAL + lib.wbkgdset(self._win, _chtype(ch) | attr) + return None + + def border(self, ls=0, rs=0, ts=0, bs=0, tl=0, tr=0, bl=0, br=0): + lib.wborder(self._win, + _chtype(ls), _chtype(rs), _chtype(ts), _chtype(bs), + _chtype(tl), _chtype(tr), _chtype(bl), _chtype(br)) + return None + + def box(self, vertint=0, horint=0): + lib.box(self._win, vertint, horint) + return None + + @_argspec(1, 1, 2) + def chgat(self, y, x, num, attr=None): + # These optional args are in a weird order. + if attr is None: + attr = num + num = -1 + + color = ((attr >> 8) & 0xff) + attr = attr - (color << 8) + + if y is not None: + code = lib.mvwchgat(self._win, y, x, num, attr, color, ffi.NULL) + lib.touchline(self._win, y, 1) + else: + yy, _ = self.getyx() + code = lib.wchgat(self._win, num, attr, color, ffi.NULL) + lib.touchline(self._win, yy, 1) + return _check_ERR(code, "chgat") + + def delch(self, *args): + if len(args) == 0: + code = lib.wdelch(self._win) + elif len(args) == 2: + code = lib.mvwdelch(self._win, *args) + else: + raise error("delch requires 0 or 2 arguments") + return _check_ERR(code, "[mv]wdelch") + + def derwin(self, *args): + nlines = 0 + ncols = 0 + if len(args) == 2: + begin_y, begin_x = args + elif len(args) == 4: + nlines, ncols, begin_y, begin_x = args + else: + raise error("derwin requires 2 or 4 arguments") + + win = lib.derwin(self._win, nlines, ncols, begin_y, begin_x) + return Window(_check_NULL(win)) + + def echochar(self, ch, attr=None): + if attr is None: + attr = lib.A_NORMAL + ch = _chtype(ch) + + if lib._m_ispad(self._win): + code = lib.pechochar(self._win, ch | attr) + else: + code = lib.wechochar(self._win, ch | attr) + return _check_ERR(code, "echochar") + + if lib._m_NCURSES_MOUSE_VERSION: + enclose = _mk_w_return_val("wenclose") + + getbkgd = _mk_w_return_val("getbkgd") + + def getch(self, *args): + if len(args) == 0: + val = lib.wgetch(self._win) + elif len(args) == 2: + val = lib.mvwgetch(self._win, *args) + else: + raise error("getch requires 0 or 2 arguments") + return val + + def getkey(self, *args): + if len(args) == 0: + val = lib.wgetch(self._win) + elif len(args) == 2: + val = lib.mvwgetch(self._win, *args) + else: + raise error("getkey requires 0 or 2 arguments") + + if val == lib.ERR: + raise error("no input") + elif val <= 255: + return chr(val) + else: + # XXX: The following line is different if `__NetBSD__` is defined. + val = lib.keyname(val) + if val == ffi.NULL: + return "" + return ffi.string(val) + + @_argspec(0, 1, 2) + def getstr(self, y, x, n=1023): + n = min(n, 1023) + buf = ffi.new("char[1024]") # /* This should be big enough.. I hope */ + + if y is None: + val = lib.wgetnstr(self._win, buf, n) + else: + val = lib.mvwgetnstr(self._win, y, x, buf, n) + + if val == lib.ERR: + return "" + return ffi.string(buf) + + @_argspec(2, 1, 2) + def hline(self, y, x, ch, n, attr=None): + ch = _chtype(ch) + if attr is None: + attr = lib.A_NORMAL + if y is not None: + _check_ERR(lib.wmove(self._win, y, x), "wmove") + return _check_ERR(lib.whline(self._win, ch | attr, n), "hline") + + @_argspec(1, 1, 2) + def insch(self, y, x, ch, attr=None): + ch = _chtype(ch) + if attr is None: + attr = lib.A_NORMAL + if y is not None: + code = lib.mvwinsch(self._win, y, x, ch | attr) + else: + code = lib.winsch(self._win, ch | attr) + return _check_ERR(code, "insch") + + def inch(self, *args): + if len(args) == 0: + return lib.winch(self._win) + elif len(args) == 2: + return lib.mvwinch(self._win, *args) + else: + raise error("inch requires 0 or 2 arguments") + + @_argspec(0, 1, 2) + def instr(self, y, x, n=1023): + n = min(n, 1023) + buf = ffi.new("char[1024]") # /* This should be big enough.. I hope */ + if y is None: + code = lib.winnstr(self._win, buf, n) + else: + code = lib.mvwinnstr(self._win, y, x, buf, n) + + if code == lib.ERR: + return "" + return ffi.string(buf) + + @_argspec(1, 1, 2) + def insstr(self, y, x, text, attr=None): + text = _texttype(text) + if attr is not None: + attr_old = lib.getattrs(self._win) + lib.wattrset(self._win, attr) + if y is not None: + code = lib.mvwinsstr(self._win, y, x, text) + else: + code = lib.winsstr(self._win, text) + if attr is not None: + lib.wattrset(self._win, attr_old) + return _check_ERR(code, "insstr") + + @_argspec(2, 1, 2) + def insnstr(self, y, x, text, n, attr=None): + text = _texttype(text) + if attr is not None: + attr_old = lib.getattrs(self._win) + lib.wattrset(self._win, attr) + if y is not None: + code = lib.mvwinsnstr(self._win, y, x, text, n) + else: + code = lib.winsnstr(self._win, text, n) + if attr is not None: + lib.wattrset(self._win, attr_old) + return _check_ERR(code, "insnstr") + + def is_linetouched(self, line): + code = lib.is_linetouched(self._win, line) + if code == lib.ERR: + raise error("is_linetouched: line number outside of boundaries") + if code == lib.FALSE: + return False + return True + + def noutrefresh(self, *args): + if lib._m_ispad(self._win): + if len(args) != 6: + raise error( + "noutrefresh() called for a pad requires 6 arguments") + return _check_ERR(lib.pnoutrefresh(self._win, *args), + "pnoutrefresh") + else: + # XXX: Better args check here? We need zero args. + return _check_ERR(lib.wnoutrefresh(self._win, *args), + "wnoutrefresh") + + nooutrefresh = noutrefresh # "to be removed in 2.3", but in 2.7, 3.x. + + def _copywin(self, dstwin, overlay, + sminr, sminc, dminr, dminc, dmaxr, dmaxc): + return _check_ERR(lib.copywin(self._win, dstwin._win, + sminr, sminc, dminr, dminc, dmaxr, dmaxc, + overlay), "copywin") + + def overlay(self, dstwin, *args): + if len(args) == 6: + return self._copywin(dstwin, True, *args) + elif len(args) == 0: + return _check_ERR(lib.overlay(self._win, dstwin._win), "overlay") + else: + raise error("overlay requires one or seven arguments") + + def overwrite(self, dstwin, *args): + if len(args) == 6: + return self._copywin(dstwin, False, *args) + elif len(args) == 0: + return _check_ERR(lib.overwrite(self._win, dstwin._win), + "overwrite") + else: + raise error("overwrite requires one or seven arguments") + + def putwin(self, filep): + # filestar = ffi.new("FILE *", filep) + return _check_ERR(lib.putwin(self._win, filep), "putwin") + + def redrawln(self, beg, num): + return _check_ERR(lib.wredrawln(self._win, beg, num), "redrawln") + + def refresh(self, *args): + if lib._m_ispad(self._win): + if len(args) != 6: + raise error( + "noutrefresh() called for a pad requires 6 arguments") + return _check_ERR(lib.prefresh(self._win, *args), "prefresh") + else: + # XXX: Better args check here? We need zero args. + return _check_ERR(lib.wrefresh(self._win, *args), "wrefresh") + + def setscrreg(self, y, x): + return _check_ERR(lib.wsetscrreg(self._win, y, x), "wsetscrreg") + + def subwin(self, *args): + nlines = 0 + ncols = 0 + if len(args) == 2: + begin_y, begin_x = args + elif len(args) == 4: + nlines, ncols, begin_y, begin_x = args + else: + raise error("subwin requires 2 or 4 arguments") + + if lib._m_ispad(self._win): + win = lib.subpad(self._win, nlines, ncols, begin_y, begin_x) + else: + win = lib.subwin(self._win, nlines, ncols, begin_y, begin_x) + return Window(_check_NULL(win)) + + def scroll(self, nlines=None): + if nlines is None: + return _check_ERR(lib.scroll(self._win), "scroll") + else: + return _check_ERR(lib.wscrl(self._win, nlines), "scroll") + + def touchline(self, st, cnt, val=None): + if val is None: + return _check_ERR(lib.touchline(self._win, st, cnt), "touchline") + else: + return _check_ERR(lib.wtouchln(self._win, st, cnt, val), + "touchline") + + @_argspec(2, 1, 2) + def vline(self, y, x, ch, n, attr=None): + ch = _chtype(ch) + if attr is None: + attr = lib.A_NORMAL + if y is not None: + _check_ERR(lib.wmove(self._win, y, x), "wmove") + return _check_ERR(lib.wvline(self._win, ch | attr, n), "vline") + + +beep = _mk_no_return("beep") +def_prog_mode = _mk_no_return("def_prog_mode") +def_shell_mode = _mk_no_return("def_shell_mode") +doupdate = _mk_no_return("doupdate") +endwin = _mk_no_return("endwin") +flash = _mk_no_return("flash") +nocbreak = _mk_no_return("nocbreak") +noecho = _mk_no_return("noecho") +nonl = _mk_no_return("nonl") +noraw = _mk_no_return("noraw") +reset_prog_mode = _mk_no_return("reset_prog_mode") +reset_shell_mode = _mk_no_return("reset_shell_mode") +resetty = _mk_no_return("resetty") +savetty = _mk_no_return("savetty") + +cbreak = _mk_flag_func("cbreak") +echo = _mk_flag_func("echo") +nl = _mk_flag_func("nl") +raw = _mk_flag_func("raw") + +baudrate = _mk_return_val("baudrate") +termattrs = _mk_return_val("termattrs") + +termname = _mk_return_val("termname") +longname = _mk_return_val("longname") + +can_change_color = _mk_return_val("can_change_color") +has_colors = _mk_return_val("has_colors") +has_ic = _mk_return_val("has_ic") +has_il = _mk_return_val("has_il") +isendwin = _mk_return_val("isendwin") +flushinp = _mk_return_val("flushinp") +noqiflush = _mk_return_val("noqiflush") + + +def filter(): + lib.filter() + return None + + +def color_content(color): + _ensure_initialised_color() + r, g, b = ffi.new("short *"), ffi.new("short *"), ffi.new("short *") + if lib.color_content(color, r, g, b) == lib.ERR: + raise error("Argument 1 was out of range. Check value of COLORS.") + return (r[0], g[0], b[0]) + + +def color_pair(n): + _ensure_initialised_color() + return (n << 8) + + +def curs_set(vis): + _ensure_initialised() + val = lib.curs_set(vis) + _check_ERR(val, "curs_set") + return val + + +def delay_output(ms): + _ensure_initialised() + return _check_ERR(lib.delay_output(ms), "delay_output") + + +def erasechar(): + _ensure_initialised() + return lib.erasechar() + + +def getsyx(): + _ensure_initialised() + yx = ffi.new("int[2]") + lib._m_getsyx(yx) + return (yx[0], yx[1]) + + +if lib._m_NCURSES_MOUSE_VERSION: + + def getmouse(): + _ensure_initialised() + mevent = ffi.new("MEVENT *") + _check_ERR(lib.getmouse(mevent), "getmouse") + return (mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate) + + def ungetmouse(id, x, y, z, bstate): + _ensure_initialised() + mevent = ffi.new("MEVENT *") + mevent.id, mevent.x, mevent.y, mevent.z, mevent.bstate = ( + id, x, y, z, bstate) + return _check_ERR(lib.ungetmouse(mevent), "ungetmouse") + + +def getwin(filep): + return Window(_check_NULL(lib.getwin(filep))) + + +def halfdelay(tenths): + _ensure_initialised() + return _check_ERR(lib.halfdelay(tenths), "halfdelay") + + +if not lib._m_STRICT_SYSV_CURSES: + def has_key(ch): + _ensure_initialised() + return lib.has_key(ch) + + +def init_color(color, r, g, b): + _ensure_initialised_color() + return _check_ERR(lib.init_color(color, r, g, b), "init_color") + + +def init_pair(pair, f, b): + _ensure_initialised_color() + return _check_ERR(lib.init_pair(pair, f, b), "init_pair") + + +def _mk_acs(name, ichar): + if len(ichar) == 1: + globals()[name] = lib.acs_map[ord(ichar)] + else: + globals()[name] = globals()[ichar] + + +def _map_acs(): + _mk_acs("ACS_ULCORNER", 'l') + _mk_acs("ACS_LLCORNER", 'm') + _mk_acs("ACS_URCORNER", 'k') + _mk_acs("ACS_LRCORNER", 'j') + _mk_acs("ACS_LTEE", 't') + _mk_acs("ACS_RTEE", 'u') + _mk_acs("ACS_BTEE", 'v') + _mk_acs("ACS_TTEE", 'w') + _mk_acs("ACS_HLINE", 'q') + _mk_acs("ACS_VLINE", 'x') + _mk_acs("ACS_PLUS", 'n') + _mk_acs("ACS_S1", 'o') + _mk_acs("ACS_S9", 's') + _mk_acs("ACS_DIAMOND", '`') + _mk_acs("ACS_CKBOARD", 'a') + _mk_acs("ACS_DEGREE", 'f') + _mk_acs("ACS_PLMINUS", 'g') + _mk_acs("ACS_BULLET", '~') + _mk_acs("ACS_LARROW", ',') + _mk_acs("ACS_RARROW", '+') + _mk_acs("ACS_DARROW", '.') + _mk_acs("ACS_UARROW", '-') + _mk_acs("ACS_BOARD", 'h') + _mk_acs("ACS_LANTERN", 'i') + _mk_acs("ACS_BLOCK", '0') + _mk_acs("ACS_S3", 'p') + _mk_acs("ACS_S7", 'r') + _mk_acs("ACS_LEQUAL", 'y') + _mk_acs("ACS_GEQUAL", 'z') + _mk_acs("ACS_PI", '{') + _mk_acs("ACS_NEQUAL", '|') + _mk_acs("ACS_STERLING", '}') + _mk_acs("ACS_BSSB", "ACS_ULCORNER") + _mk_acs("ACS_SSBB", "ACS_LLCORNER") + _mk_acs("ACS_BBSS", "ACS_URCORNER") + _mk_acs("ACS_SBBS", "ACS_LRCORNER") + _mk_acs("ACS_SBSS", "ACS_RTEE") + _mk_acs("ACS_SSSB", "ACS_LTEE") + _mk_acs("ACS_SSBS", "ACS_BTEE") + _mk_acs("ACS_BSSS", "ACS_TTEE") + _mk_acs("ACS_BSBS", "ACS_HLINE") + _mk_acs("ACS_SBSB", "ACS_VLINE") + _mk_acs("ACS_SSSS", "ACS_PLUS") + + +def initscr(): + if _initialised: + lib.wrefresh(lib.stdscr) + return Window(lib.stdscr) + + win = _check_NULL(lib.initscr()) + globals()['_initialised_setupterm'] = True + globals()['_initialised'] = True + + _map_acs() + + globals()["LINES"] = lib.LINES + globals()["COLS"] = lib.COLS + + return Window(win) + + +def setupterm(term=None, fd=-1): + if fd == -1: + # XXX: Check for missing stdout here? + fd = sys.stdout.fileno() + + if _initialised_setupterm: + return None + + if term is None: + term = ffi.NULL + err = ffi.new("int *") + if lib.setupterm(term, fd, err) == lib.ERR: + err = err[0] + if err == 0: + raise error("setupterm: could not find terminal") + elif err == -1: + raise error("setupterm: could not find terminfo database") + else: + raise error("setupterm: unknown error") + + globals()["_initialised_setupterm"] = True + return None + + +def intrflush(ch): + _ensure_initialised() + return _check_ERR(lib.intrflush(ffi.NULL, ch), "intrflush") + + +# XXX: #ifdef HAVE_CURSES_IS_TERM_RESIZED +def is_term_resized(lines, columns): + _ensure_initialised() + return lib.is_term_resized(lines, columns) + + +if not lib._m_NetBSD: + def keyname(ch): + _ensure_initialised() + if ch < 0: + raise error("invalid key number") + knp = lib.keyname(ch) + if knp == ffi.NULL: + return "" + return ffi.string(knp) + + +def killchar(): + return lib.killchar() + + +def meta(ch): + return _check_ERR(lib.meta(lib.stdscr, ch), "meta") + + +if lib._m_NCURSES_MOUSE_VERSION: + + def mouseinterval(interval): + _ensure_initialised() + return _check_ERR(lib.mouseinterval(interval), "mouseinterval") + + def mousemask(newmask): + _ensure_initialised() + oldmask = ffi.new("mmask_t *") + availmask = lib.mousemask(newmask, oldmask) + return (availmask, oldmask) + + +def napms(ms): + _ensure_initialised() + return lib.napms(ms) + + +def newpad(nlines, ncols): + _ensure_initialised() + return Window(_check_NULL(lib.newpad(nlines, ncols))) + + +def newwin(nlines, ncols, begin_y=None, begin_x=None): + _ensure_initialised() + if begin_x is None: + if begin_y is not None: + raise error("newwin requires 2 or 4 arguments") + begin_y = begin_x = 0 + + return Window(_check_NULL(lib.newwin(nlines, ncols, begin_y, begin_x))) + + +def pair_content(pair): + _ensure_initialised_color() + f = ffi.new("short *") + b = ffi.new("short *") + if lib.pair_content(pair, f, b) == lib.ERR: + raise error("Argument 1 was out of range. (1..COLOR_PAIRS-1)") + return (f, b) + + +def pair_number(pairvalue): + _ensure_initialised_color() + return (pairvalue & lib.A_COLOR) >> 8 + + +def putp(text): + text = _texttype(text) + return _check_ERR(lib.putp(text), "putp") + + +def qiflush(flag=True): + _ensure_initialised() + if flag: + lib.qiflush() + else: + lib.noqiflush() + return None + + +# XXX: Do something about the following? +# /* Internal helper used for updating curses.LINES, curses.COLS, _curses.LINES +# * and _curses.COLS */ +# #if defined(HAVE_CURSES_RESIZETERM) || defined(HAVE_CURSES_RESIZE_TERM) +# static int +# update_lines_cols(void) +# { +# PyObject *o; +# PyObject *m = PyImport_ImportModuleNoBlock("curses"); + +# if (!m) +# return 0; + +# o = PyInt_FromLong(LINES); +# if (!o) { +# Py_DECREF(m); +# return 0; +# } +# if (PyObject_SetAttrString(m, "LINES", o)) { +# Py_DECREF(m); +# Py_DECREF(o); +# return 0; +# } +# if (PyDict_SetItemString(ModDict, "LINES", o)) { +# Py_DECREF(m); +# Py_DECREF(o); +# return 0; +# } +# Py_DECREF(o); +# o = PyInt_FromLong(COLS); +# if (!o) { +# Py_DECREF(m); +# return 0; +# } +# if (PyObject_SetAttrString(m, "COLS", o)) { +# Py_DECREF(m); +# Py_DECREF(o); +# return 0; +# } +# if (PyDict_SetItemString(ModDict, "COLS", o)) { +# Py_DECREF(m); +# Py_DECREF(o); +# return 0; +# } +# Py_DECREF(o); +# Py_DECREF(m); +# return 1; +# } +# #endif + +# #ifdef HAVE_CURSES_RESIZETERM +# static PyObject * +# PyCurses_ResizeTerm(PyObject *self, PyObject *args) +# { +# int lines; +# int columns; +# PyObject *result; + +# PyCursesInitialised; + +# if (!PyArg_ParseTuple(args,"ii:resizeterm", &lines, &columns)) +# return NULL; + +# result = PyCursesCheckERR(resizeterm(lines, columns), "resizeterm"); +# if (!result) +# return NULL; +# if (!update_lines_cols()) +# return NULL; +# return result; +# } + +# #endif + +# #ifdef HAVE_CURSES_RESIZE_TERM +# static PyObject * +# PyCurses_Resize_Term(PyObject *self, PyObject *args) +# { +# int lines; +# int columns; + +# PyObject *result; + +# PyCursesInitialised; + +# if (!PyArg_ParseTuple(args,"ii:resize_term", &lines, &columns)) +# return NULL; + +# result = PyCursesCheckERR(resize_term(lines, columns), "resize_term"); +# if (!result) +# return NULL; +# if (!update_lines_cols()) +# return NULL; +# return result; +# } +# #endif /* HAVE_CURSES_RESIZE_TERM */ + + +def setsyx(y, x): + _ensure_initialised() + lib.setsyx(y, x) + return None + + +def start_color(): + _check_ERR(lib.start_color(), "start_color") + globals()["COLORS"] = lib.COLORS + globals()["COLOR_PAIRS"] = lib.COLOR_PAIRS + globals()["_initialised_color"] = True + return None + + +def tigetflag(capname): + _ensure_initialised_setupterm() + return lib.tigetflag(capname) + + +def tigetnum(capname): + _ensure_initialised_setupterm() + return lib.tigetnum(capname) + + +def tigetstr(capname): + _ensure_initialised_setupterm() + val = lib.tigetstr(capname) + if int(ffi.cast("intptr_t", val)) in (0, -1): + return None + return ffi.string(val) + + +def tparm(fmt, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0): + args = [ffi.cast("int", i) for i in (i1, i2, i3, i4, i5, i6, i7, i8, i9)] + result = lib.tparm(fmt, *args) + if result == ffi.NULL: + raise error("tparm() returned NULL") + return ffi.string(result) + + +def typeahead(fd): + _ensure_initialised() + return _check_ERR(lib.typeahead(fd), "typeahead") + + +def unctrl(ch): + _ensure_initialised() + return lib.unctrl(_chtype(ch)) + + +def ungetch(ch): + _ensure_initialised() + return _check_ERR(lib.ungetch(_chtype(ch)), "ungetch") + + +def use_env(flag): + lib.use_env(flag) + return None + + +if not lib._m_STRICT_SYSV_CURSES: + + def use_default_colors(): + _ensure_initialised_color() + return _check_ERR(lib.use_default_colors(), "use_default_colors") diff --git a/demo/_curses_build.py b/demo/_curses_build.py new file mode 100644 index 0000000..1a1a3ec --- /dev/null +++ b/demo/_curses_build.py @@ -0,0 +1,327 @@ +import sys +if sys.platform == 'win32': + #This module does not exist in windows + raise ImportError('No module named _curses') + +from cffi import FFI + +ffi = FFI() + +ffi.cdef(""" +typedef ... WINDOW; +typedef ... SCREEN; +typedef unsigned long... mmask_t; +typedef unsigned char bool; +typedef unsigned long... chtype; +typedef chtype attr_t; + +typedef struct +{ + short id; /* ID to distinguish multiple devices */ + int x, y, z; /* event coordinates (character-cell) */ + mmask_t bstate; /* button state bits */ +} +MEVENT; + +static const int ERR, OK; +static const int TRUE, FALSE; +static const int KEY_MIN, KEY_MAX; + +static const int COLOR_BLACK; +static const int COLOR_RED; +static const int COLOR_GREEN; +static const int COLOR_YELLOW; +static const int COLOR_BLUE; +static const int COLOR_MAGENTA; +static const int COLOR_CYAN; +static const int COLOR_WHITE; + +static const chtype A_ATTRIBUTES; +static const chtype A_NORMAL; +static const chtype A_STANDOUT; +static const chtype A_UNDERLINE; +static const chtype A_REVERSE; +static const chtype A_BLINK; +static const chtype A_DIM; +static const chtype A_BOLD; +static const chtype A_ALTCHARSET; +static const chtype A_INVIS; +static const chtype A_PROTECT; +static const chtype A_CHARTEXT; +static const chtype A_COLOR; + +static const int BUTTON1_RELEASED; +static const int BUTTON1_PRESSED; +static const int BUTTON1_CLICKED; +static const int BUTTON1_DOUBLE_CLICKED; +static const int BUTTON1_TRIPLE_CLICKED; +static const int BUTTON2_RELEASED; +static const int BUTTON2_PRESSED; +static const int BUTTON2_CLICKED; +static const int BUTTON2_DOUBLE_CLICKED; +static const int BUTTON2_TRIPLE_CLICKED; +static const int BUTTON3_RELEASED; +static const int BUTTON3_PRESSED; +static const int BUTTON3_CLICKED; +static const int BUTTON3_DOUBLE_CLICKED; +static const int BUTTON3_TRIPLE_CLICKED; +static const int BUTTON4_RELEASED; +static const int BUTTON4_PRESSED; +static const int BUTTON4_CLICKED; +static const int BUTTON4_DOUBLE_CLICKED; +static const int BUTTON4_TRIPLE_CLICKED; +static const int BUTTON_SHIFT; +static const int BUTTON_CTRL; +static const int BUTTON_ALT; +static const int ALL_MOUSE_EVENTS; +static const int REPORT_MOUSE_POSITION; + +int setupterm(char *, int, int *); + +WINDOW *stdscr; +int COLORS; +int COLOR_PAIRS; +int COLS; +int LINES; + +int baudrate(void); +int beep(void); +int box(WINDOW *, chtype, chtype); +bool can_change_color(void); +int cbreak(void); +int clearok(WINDOW *, bool); +int color_content(short, short*, short*, short*); +int copywin(const WINDOW*, WINDOW*, int, int, int, int, int, int, int); +int curs_set(int); +int def_prog_mode(void); +int def_shell_mode(void); +int delay_output(int); +int delwin(WINDOW *); +WINDOW * derwin(WINDOW *, int, int, int, int); +int doupdate(void); +int echo(void); +int endwin(void); +char erasechar(void); +void filter(void); +int flash(void); +int flushinp(void); +chtype getbkgd(WINDOW *); +WINDOW * getwin(FILE *); +int halfdelay(int); +bool has_colors(void); +bool has_ic(void); +bool has_il(void); +void idcok(WINDOW *, bool); +int idlok(WINDOW *, bool); +void immedok(WINDOW *, bool); +WINDOW * initscr(void); +int init_color(short, short, short, short); +int init_pair(short, short, short); +int intrflush(WINDOW *, bool); +bool isendwin(void); +bool is_linetouched(WINDOW *, int); +bool is_wintouched(WINDOW *); +const char * keyname(int); +int keypad(WINDOW *, bool); +char killchar(void); +int leaveok(WINDOW *, bool); +char * longname(void); +int meta(WINDOW *, bool); +int mvderwin(WINDOW *, int, int); +int mvwaddch(WINDOW *, int, int, const chtype); +int mvwaddnstr(WINDOW *, int, int, const char *, int); +int mvwaddstr(WINDOW *, int, int, const char *); +int mvwchgat(WINDOW *, int, int, int, attr_t, short, const void *); +int mvwdelch(WINDOW *, int, int); +int mvwgetch(WINDOW *, int, int); +int mvwgetnstr(WINDOW *, int, int, char *, int); +int mvwin(WINDOW *, int, int); +chtype mvwinch(WINDOW *, int, int); +int mvwinnstr(WINDOW *, int, int, char *, int); +int mvwinsch(WINDOW *, int, int, chtype); +int mvwinsnstr(WINDOW *, int, int, const char *, int); +int mvwinsstr(WINDOW *, int, int, const char *); +int napms(int); +WINDOW * newpad(int, int); +WINDOW * newwin(int, int, int, int); +int nl(void); +int nocbreak(void); +int nodelay(WINDOW *, bool); +int noecho(void); +int nonl(void); +void noqiflush(void); +int noraw(void); +int notimeout(WINDOW *, bool); +int overlay(const WINDOW*, WINDOW *); +int overwrite(const WINDOW*, WINDOW *); +int pair_content(short, short*, short*); +int pechochar(WINDOW *, const chtype); +int pnoutrefresh(WINDOW*, int, int, int, int, int, int); +int prefresh(WINDOW *, int, int, int, int, int, int); +int putwin(WINDOW *, FILE *); +void qiflush(void); +int raw(void); +int redrawwin(WINDOW *); +int resetty(void); +int reset_prog_mode(void); +int reset_shell_mode(void); +int savetty(void); +int scroll(WINDOW *); +int scrollok(WINDOW *, bool); +int start_color(void); +WINDOW * subpad(WINDOW *, int, int, int, int); +WINDOW * subwin(WINDOW *, int, int, int, int); +int syncok(WINDOW *, bool); +chtype termattrs(void); +char * termname(void); +int touchline(WINDOW *, int, int); +int touchwin(WINDOW *); +int typeahead(int); +int ungetch(int); +int untouchwin(WINDOW *); +void use_env(bool); +int waddch(WINDOW *, const chtype); +int waddnstr(WINDOW *, const char *, int); +int waddstr(WINDOW *, const char *); +int wattron(WINDOW *, int); +int wattroff(WINDOW *, int); +int wattrset(WINDOW *, int); +int wbkgd(WINDOW *, chtype); +void wbkgdset(WINDOW *, chtype); +int wborder(WINDOW *, chtype, chtype, chtype, chtype, + chtype, chtype, chtype, chtype); +int wchgat(WINDOW *, int, attr_t, short, const void *); +int wclear(WINDOW *); +int wclrtobot(WINDOW *); +int wclrtoeol(WINDOW *); +void wcursyncup(WINDOW *); +int wdelch(WINDOW *); +int wdeleteln(WINDOW *); +int wechochar(WINDOW *, const chtype); +int werase(WINDOW *); +int wgetch(WINDOW *); +int wgetnstr(WINDOW *, char *, int); +int whline(WINDOW *, chtype, int); +chtype winch(WINDOW *); +int winnstr(WINDOW *, char *, int); +int winsch(WINDOW *, chtype); +int winsdelln(WINDOW *, int); +int winsertln(WINDOW *); +int winsnstr(WINDOW *, const char *, int); +int winsstr(WINDOW *, const char *); +int wmove(WINDOW *, int, int); +int wresize(WINDOW *, int, int); +int wnoutrefresh(WINDOW *); +int wredrawln(WINDOW *, int, int); +int wrefresh(WINDOW *); +int wscrl(WINDOW *, int); +int wsetscrreg(WINDOW *, int, int); +int wstandout(WINDOW *); +int wstandend(WINDOW *); +void wsyncdown(WINDOW *); +void wsyncup(WINDOW *); +void wtimeout(WINDOW *, int); +int wtouchln(WINDOW *, int, int, int); +int wvline(WINDOW *, chtype, int); +int tigetflag(char *); +int tigetnum(char *); +char * tigetstr(char *); +int putp(const char *); +char * tparm(const char *, ...); +int getattrs(const WINDOW *); +int getcurx(const WINDOW *); +int getcury(const WINDOW *); +int getbegx(const WINDOW *); +int getbegy(const WINDOW *); +int getmaxx(const WINDOW *); +int getmaxy(const WINDOW *); +int getparx(const WINDOW *); +int getpary(const WINDOW *); + +int getmouse(MEVENT *); +int ungetmouse(MEVENT *); +mmask_t mousemask(mmask_t, mmask_t *); +bool wenclose(const WINDOW *, int, int); +int mouseinterval(int); + +void setsyx(int y, int x); +const char *unctrl(chtype); +int use_default_colors(void); + +int has_key(int); +bool is_term_resized(int, int); + +#define _m_STRICT_SYSV_CURSES ... +#define _m_NCURSES_MOUSE_VERSION ... +#define _m_NetBSD ... +int _m_ispad(WINDOW *); + +chtype acs_map[]; + +// For _curses_panel: + +typedef ... PANEL; + +WINDOW *panel_window(const PANEL *); +void update_panels(void); +int hide_panel(PANEL *); +int show_panel(PANEL *); +int del_panel(PANEL *); +int top_panel(PANEL *); +int bottom_panel(PANEL *); +PANEL *new_panel(WINDOW *); +PANEL *panel_above(const PANEL *); +PANEL *panel_below(const PANEL *); +int set_panel_userptr(PANEL *, void *); +const void *panel_userptr(const PANEL *); +int move_panel(PANEL *, int, int); +int replace_panel(PANEL *,WINDOW *); +int panel_hidden(const PANEL *); + +void _m_getsyx(int *yx); +""") + + +ffi.set_source("_curses_cffi", """ +#ifdef __APPLE__ +/* the following define is necessary for OS X 10.6+; without it, the + Apple-supplied ncurses.h sets NCURSES_OPAQUE to 1, and then Python + can't get at the WINDOW flags field. */ +#define NCURSES_OPAQUE 0 +#endif + +#include <ncurses.h> +#include <panel.h> +#include <term.h> + +#if defined STRICT_SYSV_CURSES +#define _m_STRICT_SYSV_CURSES TRUE +#else +#define _m_STRICT_SYSV_CURSES FALSE +#endif + +#if defined NCURSES_MOUSE_VERSION +#define _m_NCURSES_MOUSE_VERSION TRUE +#else +#define _m_NCURSES_MOUSE_VERSION FALSE +#endif + +#if defined __NetBSD__ +#define _m_NetBSD TRUE +#else +#define _m_NetBSD FALSE +#endif + +int _m_ispad(WINDOW *win) { + // <curses.h> may not have _flags (and possibly _ISPAD), + // but for now let's assume that <ncurses.h> always has it + return (win->_flags & _ISPAD); +} + +void _m_getsyx(int *yx) { + getsyx(yx[0], yx[1]); +} +""", libraries=['ncurses', 'panel']) + +if __name__ == '__main__': + ffi.compile() diff --git a/demo/_curses_setup.py b/demo/_curses_setup.py new file mode 100644 index 0000000..ec3d20e --- /dev/null +++ b/demo/_curses_setup.py @@ -0,0 +1,13 @@ +from setuptools import setup + +setup( + name="_curses", + version="0.1", + py_modules=["_curses"], + setup_requires=["cffi>=1.0.dev0"], + cffi_modules=[ + "_curses_build.py:ffi", + ], + install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? + zip_safe=False, +) diff --git a/demo/api.py b/demo/api.py new file mode 100644 index 0000000..8cc6407 --- /dev/null +++ b/demo/api.py @@ -0,0 +1,62 @@ +import cffi +from cffi import FFI + +class PythonFFI(FFI): + + def __init__(self, backend=None): + FFI.__init__(self, backend=backend) + self._pyexports = {} + + def pyexport(self, signature): + tp = self._typeof(signature, consider_function_as_funcptr=True) + def decorator(func): + name = func.__name__ + if name in self._pyexports: + raise cffi.CDefError("duplicate pyexport'ed function %r" + % (name,)) + callback_var = self.getctype(tp, name) + self.cdef("%s;" % callback_var) + self._pyexports[name] = _PyExport(tp, func) + return decorator + + def verify(self, source='', **kwargs): + extras = [] + pyexports = sorted(self._pyexports.items()) + for name, export in pyexports: + callback_var = self.getctype(export.tp, name) + extras.append("%s;" % callback_var) + extras.append(source) + source = '\n'.join(extras) + lib = FFI.verify(self, source, **kwargs) + for name, export in pyexports: + cb = self.callback(export.tp, export.func) + export.cb = cb + setattr(lib, name, cb) + return lib + + +class _PyExport(object): + def __init__(self, tp, func): + self.tp = tp + self.func = func + + +if __name__ == '__main__': + ffi = PythonFFI() + + @ffi.pyexport("int(int)") + def add1(n): + print n + return n + 1 + + ffi.cdef(""" + int f(int); + """) + + lib = ffi.verify(""" + int f(int x) { + return add1(add1(x)); + } + """) + + assert lib.f(5) == 7 diff --git a/demo/bsdopendirtype.py b/demo/bsdopendirtype.py new file mode 100644 index 0000000..75a996a --- /dev/null +++ b/demo/bsdopendirtype.py @@ -0,0 +1,48 @@ +from _bsdopendirtype import ffi, lib + + +def _posix_error(): + raise OSError(ffi.errno, os.strerror(ffi.errno)) + +_dtype_to_smode = { + lib.DT_BLK: 0o060000, + lib.DT_CHR: 0o020000, + lib.DT_DIR: 0o040000, + lib.DT_FIFO: 0o010000, + lib.DT_LNK: 0o120000, + lib.DT_REG: 0o100000, + lib.DT_SOCK: 0o140000, +} + +def opendir(dir): + if len(dir) == 0: + dir = b'.' + dirname = dir + if not dirname.endswith(b'/'): + dirname += b'/' + dirp = lib.opendir(dir) + if dirp == ffi.NULL: + raise _posix_error() + try: + while True: + ffi.errno = 0 + dirent = lib.readdir(dirp) + if dirent == ffi.NULL: + if ffi.errno != 0: + raise _posix_error() + return + name = ffi.string(dirent.d_name) + if name == b'.' or name == b'..': + continue + name = dirname + name + try: + smode = _dtype_to_smode[dirent.d_type] + except KeyError: + smode = os.lstat(name).st_mode + yield name, smode + finally: + lib.closedir(dirp) + +if __name__ == '__main__': + for name, smode in opendir(b'/tmp'): + print(hex(smode), name) diff --git a/demo/bsdopendirtype_build.py b/demo/bsdopendirtype_build.py new file mode 100644 index 0000000..3c5bb0b --- /dev/null +++ b/demo/bsdopendirtype_build.py @@ -0,0 +1,23 @@ +from cffi import FFI + +ffibuilder = FFI() +ffibuilder.cdef(""" + typedef ... DIR; + struct dirent { + unsigned char d_type; /* type of file */ + char d_name[]; /* filename */ + ...; + }; + DIR *opendir(const char *name); + int closedir(DIR *dirp); + struct dirent *readdir(DIR *dirp); + static const int DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK; +""") + +ffibuilder.set_source("_bsdopendirtype", """ + #include <sys/types.h> + #include <dirent.h> +""") + +if __name__ == '__main__': + ffibuilder.compile(verbose=True) diff --git a/demo/bsdopendirtype_setup.py b/demo/bsdopendirtype_setup.py new file mode 100644 index 0000000..30a6cfb --- /dev/null +++ b/demo/bsdopendirtype_setup.py @@ -0,0 +1,13 @@ +from setuptools import setup + +setup( + name="example", + version="0.1", + py_modules=["bsdopendirtype"], + setup_requires=["cffi>=1.0.dev0"], + cffi_modules=[ + "bsdopendirtype_build.py:ffibuilder", + ], + install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? + zip_safe=False, +) diff --git a/demo/btrfs-snap.py b/demo/btrfs-snap.py new file mode 100644 index 0000000..fceeaa1 --- /dev/null +++ b/demo/btrfs-snap.py @@ -0,0 +1,52 @@ +""" +btrfs-snap.py: source target newname + +creates a exactly named snapshots and bails out if they exist +""" + +import argparse +import fcntl +import os +import sys + +import cffi + +ffi = cffi.FFI() + +ffi.cdef(""" + #define BTRFS_IOC_SNAP_CREATE_V2 ... + struct btrfs_ioctl_vol_args_v2 { + int64_t fd; + char name[]; + ...; + }; +""") + +ffi.set_source("_btrfs_cffi", "#include <btrfs/ioctl.h>") +ffi.compile() + +# ____________________________________________________________ + + +from _btrfs_cffi import ffi, lib + +parser = argparse.ArgumentParser(usage=__doc__.strip()) +parser.add_argument('source', help='source subvolume') +parser.add_argument('target', help='target directory') +parser.add_argument('newname', help='name of the new snapshot') +opts = parser.parse_args() + +source = os.open(opts.source, os.O_DIRECTORY) +target = os.open(opts.target, os.O_DIRECTORY) + + +args = ffi.new('struct btrfs_ioctl_vol_args_v2 *') +args.name = opts.newname +args.fd = source +args_buffer = ffi.buffer(args) +try: + fcntl.ioctl(target, lib.BTRFS_IOC_SNAP_CREATE_V2, args_buffer) +except IOError as e: + print e + sys.exit(1) + diff --git a/demo/cffi-cocoa.py b/demo/cffi-cocoa.py new file mode 100644 index 0000000..9e86d99 --- /dev/null +++ b/demo/cffi-cocoa.py @@ -0,0 +1,102 @@ +# Based on http://cocoawithlove.com/2010/09/minimalist-cocoa-programming.html +# by Juraj Sukop. This demo was eventually expanded into a more complete +# Cocoa library available at https://bitbucket.org/sukop/nspython . + +from cffi import FFI + +ffi = FFI() +ffi.cdef(''' + + typedef signed char BOOL; + + typedef long NSInteger; + typedef unsigned long NSUInteger; + typedef NSInteger NSApplicationActivationPolicy; + typedef NSUInteger NSBackingStoreType; + typedef NSUInteger NSStringEncoding; + + typedef double CGFloat; + struct CGPoint { + CGFloat x; + CGFloat y; + }; + typedef struct CGPoint CGPoint; + struct CGSize { + CGFloat width; + CGFloat height; + }; + typedef struct CGSize CGSize; + struct CGRect { + CGPoint origin; + CGSize size; + }; + typedef struct CGRect CGRect; + + typedef CGPoint NSPoint; + typedef CGSize NSSize; + typedef CGRect NSRect; + + typedef struct objc_class *Class; + typedef struct objc_object { + Class isa; + } *id; + typedef struct objc_selector *SEL; + + SEL sel_registerName(const char *str); + id objc_getClass(const char *name); + id objc_msgSend(id theReceiver, SEL theSelector, ...); + +''') + +objc = ffi.dlopen('objc') +appkit = ffi.dlopen('AppKit') + +nil = ffi.NULL +YES = ffi.cast('BOOL', 1) +NO = ffi.cast('BOOL', 0) + +NSASCIIStringEncoding = ffi.cast('NSStringEncoding', 1) +NSApplicationActivationPolicyRegular = ffi.cast('NSApplicationActivationPolicy', 0) +NSTitledWindowMask = ffi.cast('NSUInteger', 1) +NSBackingStoreBuffered = ffi.cast('NSBackingStoreType', 2) + +NSMakePoint = lambda x, y: ffi.new('NSPoint *', (x, y))[0] +NSMakeRect = lambda x, y, w, h: ffi.new('NSRect *', ((x, y), (w, h)))[0] + +get, send, sel = objc.objc_getClass, objc.objc_msgSend, objc.sel_registerName +at = lambda s: send( + get('NSString'), + sel('stringWithCString:encoding:'), + ffi.new('char[]', s), NSASCIIStringEncoding) + +send(get('NSAutoreleasePool'), sel('new')) +app = send(get('NSApplication'), sel('sharedApplication')) +send(app, sel('setActivationPolicy:'), NSApplicationActivationPolicyRegular) + +menubar = send(send(get('NSMenu'), sel('new')), sel('autorelease')) +appMenuItem = send(send(get('NSMenuItem'), sel('new')), sel('autorelease')) +send(menubar, sel('addItem:'), appMenuItem) +send(app, sel('setMainMenu:'), menubar) + +appMenu = send(send(get('NSMenu'), sel('new')), sel('autorelease')) +appName = send(send(get('NSProcessInfo'), sel('processInfo')), sel('processName')) +quitTitle = send(at('Quit '), sel('stringByAppendingString:'), appName) +quitMenuItem = send(send(send( + get('NSMenuItem'), sel('alloc')), + sel('initWithTitle:action:keyEquivalent:'), + quitTitle, sel('terminate:'), at('q')), + sel('autorelease')) +send(appMenu, sel('addItem:'), quitMenuItem) +send(appMenuItem, sel('setSubmenu:'), appMenu) + +window = send(send(send( + get('NSWindow'), sel('alloc')), + sel('initWithContentRect:styleMask:backing:defer:'), + NSMakeRect(0, 0, 200, 200), NSTitledWindowMask, NSBackingStoreBuffered, NO), + sel('autorelease')) +send(window, sel('cascadeTopLeftFromPoint:'), NSMakePoint(20, 20)) +send(window, sel('setTitle:'), appName) +send(window, sel('makeKeyAndOrderFront:'), nil) + +send(app, sel('activateIgnoringOtherApps:'), YES) +send(app, sel('run')) diff --git a/demo/embedding.py b/demo/embedding.py new file mode 100644 index 0000000..b15c050 --- /dev/null +++ b/demo/embedding.py @@ -0,0 +1,21 @@ +import cffi + +ffibuilder = cffi.FFI() + +ffibuilder.embedding_api(""" + int add(int, int); +""") + +ffibuilder.embedding_init_code(""" + from _embedding_cffi import ffi + print("preparing") # printed once + + @ffi.def_extern() + def add(x, y): + print("adding %d and %d" % (x, y)) + return x + y +""") + +ffibuilder.set_source("_embedding_cffi", "") + +ffibuilder.compile(verbose=True) diff --git a/demo/embedding_test.c b/demo/embedding_test.c new file mode 100644 index 0000000..ede8cb9 --- /dev/null +++ b/demo/embedding_test.c @@ -0,0 +1,43 @@ +/* There are two options: + + =====1===== + + Link this program with _embedding_test.so. + E.g. with gcc: + + gcc -o embedding_test embedding_test.c _embedding_cffi*.so + + You must then run the executable with the right command + (LD_LIBRARY_PATH on Linux), otherwise it won't find the + _embedding_cffi*.so: + + LD_LIBRARY_PATH=. ./embedding_test + + There are platform-specific options to gcc to avoid needing + that, too. Linux: + + gcc -o embedding_test embedding_test.c _embedding_cffi*.so \ + -Wl,-rpath=\$ORIGIN/ + + =====2===== + + Compile and link the _embedding_test.c source code together with + this example (e.g. with PyPy): + + gcc -o embedding_test embedding_test.c _embedding_cffi.c \ + -I/opt/pypy/include -pthread -lpypy-c +*/ + +#include <stdio.h> + +extern int add(int x, int y); + + +int main(void) +{ + int res = add(40, 2); + printf("result: %d\n", res); + res = add(100, -5); + printf("result: %d\n", res); + return 0; +} diff --git a/demo/extern_python.py b/demo/extern_python.py new file mode 100644 index 0000000..f315cc5 --- /dev/null +++ b/demo/extern_python.py @@ -0,0 +1,26 @@ +import cffi + +ffi = cffi.FFI() + +ffi.cdef("""int my_algo(int); extern "Python" int f(int);""") + +ffi.set_source("_extern_python_cffi", """ + static int f(int); + static int my_algo(int n) { + int i, sum = 0; + for (i = 0; i < n; i++) + sum += f(i); + return sum; + } +""") + +ffi.compile() + + +from _extern_python_cffi import ffi, lib + +@ffi.def_extern() +def f(n): + return n * n + +assert lib.my_algo(10) == 0+1+4+9+16+25+36+49+64+81 diff --git a/demo/extern_python_varargs.py b/demo/extern_python_varargs.py new file mode 100644 index 0000000..ee78079 --- /dev/null +++ b/demo/extern_python_varargs.py @@ -0,0 +1,61 @@ +import cffi + +ffi = cffi.FFI() + +ffi.cdef(""" + int my_algo(int); + typedef ... va_list; + extern "Python" int f(int, va_list *); + + int fetch_int(va_list *); + double fetch_double(va_list *); + void *fetch_ptr(va_list *); +""") + +ffi.set_source("_extern_python_cffi", """ + #include <stdarg.h> + + static int f(int, va_list *); + + static int f1(int n, ...) + { + va_list ap; + va_start(ap, n); + int res = f(n, &ap); + va_end(ap); + return res; + } + + static int fetch_int(va_list *va) { return va_arg((*va), int); } + static double fetch_double(va_list *va) { return va_arg((*va), double); } + static void * fetch_ptr(va_list *va) { return va_arg((*va), void *); } + + static int my_algo(int n) { + return f1(3, n, n+1, n+2) + f1(1, &n) + f1(2, 12.3, 45.6); + } +""") + +ffi.compile() + + +from _extern_python_cffi import ffi, lib + +@ffi.def_extern() +def f(n, va): + if n == 3: + x = lib.fetch_int(va) + y = lib.fetch_int(va) + z = lib.fetch_int(va) + print (x, y, z) + elif n == 1: + ptr = lib.fetch_ptr(va) + print 'ptr to:', ffi.cast("int *", ptr)[0] + elif n == 2: + x = lib.fetch_double(va) + y = lib.fetch_double(va) + print (x, y) + else: + raise AssertionError(n) + return 14 + +print lib.my_algo(10) diff --git a/demo/fastcsv.py b/demo/fastcsv.py new file mode 100644 index 0000000..6b8d0b4 --- /dev/null +++ b/demo/fastcsv.py @@ -0,0 +1,266 @@ +import csv +import cffi + +# IN-PROGRESS. See the demo at the end of the file + + +def _make_ffi_from_dialect(dialect_name): + dialect = csv.get_dialect(dialect_name) + + ffi = cffi.FFI() + + ffi.cdef(""" + long parse_line(char *rawline, long inputlength); + """) + + d = {'quotechar': ord(dialect.quotechar), + 'quoting': int(dialect.quoting), + 'skipinitialspace': int(dialect.skipinitialspace), + 'delimiter': ord(dialect.delimiter), + 'doublequote': int(dialect.doublequote), + 'strict': int(dialect.strict), + } + if dialect.escapechar is not None: + d['is_escape_char'] = '== %d' % ord(dialect.escapechar) + else: + d['is_escape_char'] = '&& 0' + + ffi.set_source('_fastcsv_' + dialect_name, r''' + + typedef enum { + START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, + IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD, + EAT_CRNL + } ParserState; + + typedef enum { + QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE + } QuoteStyle; + + typedef struct { + ParserState state; /* current CSV parse state */ + char *field; /* build current field in here */ + int field_size; /* size of allocated buffer */ + int field_len; /* length of current field */ + int numeric_field; /* treat field as numeric */ + } ReaderObj; + + static void + parse_add_char(ReaderObj *self, char c) + { + *self->field++ = c; + } + + static void + parse_save_field(ReaderObj *self) + { + *self->field++ = 0; + } + + static int + parse_process_char(ReaderObj *self, char c) + { + switch (self->state) { + case START_RECORD: + /* start of record */ + if (c == '\0') + /* empty line - return [] */ + break; + else if (c == '\n' || c == '\r') { + self->state = EAT_CRNL; + break; + } + /* normal character - handle as START_FIELD */ + self->state = START_FIELD; + /* fallthru */ + case START_FIELD: + /* expecting field */ + if (c == '\n' || c == '\r' || c == '\0') { + /* save empty field - return [fields] */ + parse_save_field(self); + self->state = (c == '\0' ? START_RECORD : EAT_CRNL); + } + else if (c == %(quotechar)d && + %(quoting)d != QUOTE_NONE) { + /* start quoted field */ + self->state = IN_QUOTED_FIELD; + } + else if (c %(is_escape_char)s) { + /* possible escaped character */ + self->state = ESCAPED_CHAR; + } + else if (c == ' ' && %(skipinitialspace)d) + /* ignore space at start of field */ + ; + else if (c == %(delimiter)d) { + /* save empty field */ + parse_save_field(self); + } + else { + /* begin new unquoted field */ + if (%(quoting)d == QUOTE_NONNUMERIC) + self->numeric_field = 1; + parse_add_char(self, c); + self->state = IN_FIELD; + } + break; + + case ESCAPED_CHAR: + if (c == '\0') + c = '\n'; + parse_add_char(self, c); + self->state = IN_FIELD; + break; + + case IN_FIELD: + /* in unquoted field */ + if (c == '\n' || c == '\r' || c == '\0') { + /* end of line - return [fields] */ + parse_save_field(self); + self->state = (c == '\0' ? START_RECORD : EAT_CRNL); + } + else if (c %(is_escape_char)s) { + /* possible escaped character */ + self->state = ESCAPED_CHAR; + } + else if (c == %(delimiter)d) { + /* save field - wait for new field */ + parse_save_field(self); + self->state = START_FIELD; + } + else { + /* normal character - save in field */ + parse_add_char(self, c); + } + break; + + case IN_QUOTED_FIELD: + /* in quoted field */ + if (c == '\0') + ; + else if (c %(is_escape_char)s) { + /* Possible escape character */ + self->state = ESCAPE_IN_QUOTED_FIELD; + } + else if (c == %(quotechar)d && + %(quoting)d != QUOTE_NONE) { + if (%(doublequote)d) { + /* doublequote; " represented by "" */ + self->state = QUOTE_IN_QUOTED_FIELD; + } + else { + /* end of quote part of field */ + self->state = IN_FIELD; + } + } + else { + /* normal character - save in field */ + parse_add_char(self, c); + } + break; + + case ESCAPE_IN_QUOTED_FIELD: + if (c == '\0') + c = '\n'; + parse_add_char(self, c); + self->state = IN_QUOTED_FIELD; + break; + + case QUOTE_IN_QUOTED_FIELD: + /* doublequote - seen a quote in an quoted field */ + if (%(quoting)d != QUOTE_NONE && + c == %(quotechar)d) { + /* save "" as " */ + parse_add_char(self, c); + self->state = IN_QUOTED_FIELD; + } + else if (c == %(delimiter)d) { + /* save field - wait for new field */ + parse_save_field(self); + self->state = START_FIELD; + } + else if (c == '\n' || c == '\r' || c == '\0') { + /* end of line - return [fields] */ + parse_save_field(self); + self->state = (c == '\0' ? START_RECORD : EAT_CRNL); + } + else if (!%(strict)d) { + parse_add_char(self, c); + self->state = IN_FIELD; + } + else { + /* illegal */ + /*PyErr_Format(error_obj, "'%%c' expected after '%%c'", + dialect->delimiter, + dialect->quotechar);*/ + return -1; + } + break; + + case EAT_CRNL: + if (c == '\n' || c == '\r') + ; + else if (c == '\0') + self->state = START_RECORD; + else { + /*PyErr_Format(error_obj, "new-line character seen in unquoted field - do you need to open the file in universal-newline mode?");*/ + return -1; + } + break; + + } + return 0; + } + + static void + parse_reset(ReaderObj *self, char *rawline) + { + self->field = rawline; + self->state = START_RECORD; + self->numeric_field = 0; + } + + long parse_line(char *rawline, long inputlength) + { + char *p; + ReaderObj reader; + parse_reset(&reader, rawline); + + for (p=rawline; inputlength > 0; inputlength--, p++) { + if (parse_process_char(&reader, *p) < 0) + return -1; + } + if (parse_process_char(&reader, 0) < 0) + return -1; + return reader.field - rawline - 1; + } + ''' % d) + + ffi.compile() + + +def fastcsv_reader(f, dialect_name): + try: + module = __import__('_fastcsv_' + dialect_name) + except ImportError: + _make_ffi_from_dialect(dialect_name) + module = __import__('_fastcsv_' + dialect_name) + ffi, lib = module.ffi, module.lib + # + linelen = -1 + for line in f: + if linelen <= len(line): + linelen = 2 * len(line) + rawline = ffi.new("char[]", linelen) + ffi.buffer(rawline, len(line))[:] = line + n = lib.parse_line(rawline, len(line)) + assert n >= 0 + yield ffi.buffer(rawline, n)[:].split('\x00') + + +if __name__ == '__main__': + csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE) + with open('/etc/passwd', 'rb') as f: + reader = fastcsv_reader(f, 'unixpwd') + for row in reader: + print row diff --git a/demo/gmp.py b/demo/gmp.py new file mode 100644 index 0000000..44f233c --- /dev/null +++ b/demo/gmp.py @@ -0,0 +1,33 @@ +import sys +# +# This is only a demo based on the GMP library. +# There is a rather more complete (but perhaps outdated) version available at: +# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files +# + +try: + from _gmp_cffi import ffi, lib +except ImportError: + print 'run gmp_build first, then make sure the shared object is on sys.path' + sys.exit(1) + +# ffi "knows" about the declared variables and functions from the +# cdef parts of the module created from gmp_build +# lib "knows" how to call the functions from the set_source parts +# of the module. + +# ____________________________________________________________ + +a = ffi.new("mpz_t") +b = ffi.new("mpz_t") + +if len(sys.argv) < 3: + print 'call as %s bigint1, bigint2' % sys.argv[0] + sys.exit(2) + +lib.mpz_init_set_str(a, sys.argv[1], 10) # Assume decimal integers +lib.mpz_init_set_str(b, sys.argv[2], 10) # Assume decimal integers +lib.mpz_add(a, a, b) # a=a+b + +s = lib.mpz_get_str(ffi.NULL, 10, a) +print ffi.string(s) diff --git a/demo/gmp_build.py b/demo/gmp_build.py new file mode 100644 index 0000000..e1a6000 --- /dev/null +++ b/demo/gmp_build.py @@ -0,0 +1,26 @@ +import cffi + +# +# This is only a demo based on the GMP library. +# There is a rather more complete (but perhaps outdated) version available at: +# http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files +# + +ffibuilder = cffi.FFI() + +ffibuilder.cdef(""" + + typedef struct { ...; } MP_INT; + typedef MP_INT mpz_t[1]; + + int mpz_init_set_str (MP_INT *dest_integer, char *src_cstring, int base); + void mpz_add (MP_INT *sum, MP_INT *addend1, MP_INT *addend2); + char * mpz_get_str (char *string, int base, MP_INT *integer); + +""") + +ffibuilder.set_source('_gmp_cffi', "#include <gmp.h>", + libraries=['gmp', 'm']) + +if __name__ == '__main__': + ffibuilder.compile(verbose=True) diff --git a/demo/manual.c b/demo/manual.c new file mode 100644 index 0000000..5b360e8 --- /dev/null +++ b/demo/manual.c @@ -0,0 +1,166 @@ +#include "_cffi_include.h" + + +#define AA (42) +#define BB (&bb) +static int bb = 16261; + +int foo42(int a, int *b) +{ + return a - *b; +} + +int foo64(int a) +{ + return ~a; +} + +struct foo_s { + int a; +}; + +/************************************************************/ + +static void *_cffi_types[] = { + _CFFI_OP(_CFFI_OP_FUNCTION, 1), + _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), + _CFFI_OP(_CFFI_OP_POINTER, 1), + _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), + _CFFI_OP(_CFFI_OP_FUNCTION, 1), + _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), + _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), + _CFFI_OP(_CFFI_OP_STRUCT_UNION, 0), +}; + +#ifndef PYPY_VERSION +static PyObject * +_cffi_f_foo42(PyObject *self, PyObject *args) +{ + int x0; + int * x1; + Py_ssize_t datasize; + int result; + PyObject *arg0; + PyObject *arg1; + + if (!PyArg_ParseTuple(args, "OO:foo42", &arg0, &arg1)) + return NULL; + + x0 = _cffi_to_c_int(arg0, int); + if (x0 == (int)-1 && PyErr_Occurred()) + return NULL; + + datasize = _cffi_prepare_pointer_call_argument( + _cffi_types[1], arg1, (char **)&x1); + if (datasize != 0) { + if (datasize < 0) + return NULL; + x1 = alloca(datasize); + memset((void *)x1, 0, datasize); + if (_cffi_convert_array_from_object((char *)x1, _cffi_types[1], arg1) < 0) + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + _cffi_restore_errno(); + { result = foo42(x0, x1); } + _cffi_save_errno(); + Py_END_ALLOW_THREADS + + return _cffi_from_c_int(result, int); +} +#else +static int _cffi_f_foo42(int x0, int *x1) +{ + return foo42(x0, x1); +} +#endif + +#ifndef PYPY_VERSION +static PyObject * +_cffi_f_foo64(PyObject *self, PyObject *arg0) +{ + int x0; + int result; + + x0 = _cffi_to_c_int(arg0, int); + if (x0 == (int)-1 && PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS + _cffi_restore_errno(); + { result = foo64(x0); } + _cffi_save_errno(); + Py_END_ALLOW_THREADS + + return _cffi_from_c_int(result, int); +} +#else +static int _cffi_f_foo64(int x0) +{ + return foo64(x0); +} +#endif + +static int _cffi_const_AA(unsigned long long *output) +{ + *output = (unsigned long long)((AA) << 0); // integer + return (AA) <= 0; +} + +static void _cffi_const_BB(char *output) +{ + *(int **)output = BB; +} + +static const struct _cffi_global_s _cffi_globals[] = { + { "AA", &_cffi_const_AA, _CFFI_OP(_CFFI_OP_CONSTANT_INT, 0) }, + { "BB", &_cffi_const_BB, _CFFI_OP(_CFFI_OP_CONSTANT, 2) }, + { "bb", &bb, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, 1) }, + { "foo42", &_cffi_f_foo42, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 0) }, + { "foo64", &_cffi_f_foo64, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 4) }, +}; + +struct _cffi_align_foo_s { char x; struct foo_s y; }; + +static const struct _cffi_struct_union_s _cffi_struct_unions[] = { + { "foo_s", 7, 0, + sizeof(struct foo_s), + offsetof(struct _cffi_align_foo_s, y), + 1, 0 }, +}; + +static const struct _cffi_field_s _cffi_fields[] = { + { "a", offsetof(struct foo_s, a), sizeof(((struct foo_s *)0)->a), + _CFFI_OP(_CFFI_OP_NOOP, 1) }, +}; + +static const struct _cffi_type_context_s _cffi_type_context = { + _cffi_types, + _cffi_globals, + _cffi_fields, + _cffi_struct_unions, + NULL, + NULL, + 5, /* num_globals */ + 1, /* num_struct_unions */ + 0, + 0, + NULL, + 8, /* num_types */ +}; + +#ifndef PYPY_VERSION +PyMODINIT_FUNC +initmanual(void) +{ + _cffi_init("manual", 0x2601, &_cffi_type_context); +} +#else +PyMODINIT_FUNC +_cffi_pypyinit_manual(const void *p[]) +{ + p[0] = (const void *)0x2601; + p[1] = &_cffi_type_context; +} +#endif diff --git a/demo/manual2.py b/demo/manual2.py new file mode 100644 index 0000000..2986244 --- /dev/null +++ b/demo/manual2.py @@ -0,0 +1,34 @@ +import _cffi_backend + +ffi = _cffi_backend.FFI(b"manual2", + _version = 0x2601, + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x00\x09\x00\x00\x00\x0B\x00\x00\x01\x03', + _globals = (b'\xff\xff\xff\x0bAA',0,b'\xff\xff\xff\x0bBB',-1,b'\xff\xff\xff\x0bCC',2,b'\xff\xff\xff\x1fFOO',0x9999999999999999,b'\x00\x00\x00#close',0,b'\x00\x00\x05#stdout',0), + _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x00point_s',b'\x00\x00\x01\x11\xff\xff\xff\xffx',b'\x00\x00\x01\x11\xff\xff\xff\xffy'),), + _enums = (b'\x00\x00\x00\x04\x00\x00\x00\x07myenum_e\x00AA,BB,CC',), + _typenames = (b'\x00\x00\x00\x01myint_t',), +) + + + +# trying it out +lib = ffi.dlopen(None) +assert lib.AA == 0 +assert lib.BB == -1 +assert lib.FOO == 0x9999999999999999 +x = lib.close(-42) +assert x == -1 + +print lib.stdout + +print ffi.new("struct point_s *") +print ffi.offsetof("struct point_s", "x") +print ffi.offsetof("struct point_s", "y") +print ffi.new("struct point_s[CC]") +assert ffi.sizeof("struct point_s[CC]") == 2 * ffi.sizeof("struct point_s") + +print ffi.cast("enum myenum_e", 2) +print ffi.cast("myint_t", -2) +assert ffi.typeof("myint_t") == ffi.typeof("int") + +del ffi, lib diff --git a/demo/pwuid.py b/demo/pwuid.py new file mode 100644 index 0000000..dda9299 --- /dev/null +++ b/demo/pwuid.py @@ -0,0 +1,7 @@ +import sys, os + +# run pwuid_build first, then make sure the shared object is on sys.path +from _pwuid_cffi import ffi, lib + + +print ffi.string(lib.getpwuid(0).pw_name) diff --git a/demo/pwuid_build.py b/demo/pwuid_build.py new file mode 100644 index 0000000..7ef0d76 --- /dev/null +++ b/demo/pwuid_build.py @@ -0,0 +1,18 @@ +from cffi import FFI +ffi = FFI() +ffi.cdef(""" // some declarations from the man page + struct passwd { + char *pw_name; + ...; + }; + struct passwd *getpwuid(int uid); +""") + +ffi.set_source('_pwuid_cffi', """ // passed to the real C compiler +#include <sys/types.h> +#include <pwd.h> +""") + + +if __name__ == '__main__': + ffi.compile() diff --git a/demo/py.cleanup b/demo/py.cleanup new file mode 100755 index 0000000..512389f --- /dev/null +++ b/demo/py.cleanup @@ -0,0 +1,31 @@ +#! /usr/bin/env python +import sys, os, stat +from bsdopendirtype import opendir + +def clean(path): + global count + try: + content = opendir(path) + except OSError: + print >> sys.stderr, "skipping", path + return + for filename, smode in content: + if stat.S_ISDIR(smode): + clean(filename) + if filename.endswith('/__pycache__'): + try: + os.rmdir(filename) + except OSError: + pass + elif (filename.endswith('.pyc') or filename.endswith('.pyo') or + filename.endswith('.pyc~') or filename.endswith('.pyo~')): + os.unlink(filename) + count += 1 + +count = 0 + +for arg in sys.argv[1:] or ['.']: + print "cleaning path", arg, "of .pyc/.pyo/__pycache__ files" + clean(arg) + +print "%d files removed" % (count,) diff --git a/demo/pyobj.py b/demo/pyobj.py new file mode 100644 index 0000000..b40343a --- /dev/null +++ b/demo/pyobj.py @@ -0,0 +1,124 @@ + +referents = [] # list "object descriptor -> python object" +freelist = None + +def store(x): + "Store the object 'x' and returns a new object descriptor for it." + global freelist + p = freelist + if p is None: + p = len(referents) + referents.append(x) + else: + freelist = referents[p] + referents[p] = x + return p + +def discard(p): + """Discard (i.e. close) the object descriptor 'p'. + Return the original object that was attached to 'p'.""" + global freelist + x = referents[p] + referents[p] = freelist + freelist = p + return x + +class Ref(object): + """For use in 'with Ref(x) as ob': open an object descriptor + and returns it in 'ob', and close it automatically when the + 'with' statement finishes.""" + def __init__(self, x): + self.x = x + def __enter__(self): + self.p = p = store(self.x) + return p + def __exit__(self, *args): + discard(self.p) + +def count_pyobj_alive(): + result = len(referents) + p = freelist + while p is not None: + assert result > 0 + result -= 1 + p = referents[p] + return result + +# ------------------------------------------------------------ + +if __name__ == '__main__': + import api + + ffi = api.PythonFFI() + + ffi.cdef(""" + typedef int pyobj_t; + int sum_integers(pyobj_t p_list); + pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial); + """) + + @ffi.pyexport("int(pyobj_t)") + def length(p_list): + list = referents[p_list] + return len(list) + + @ffi.pyexport("int(pyobj_t, int)") + def getitem(p_list, index): + list = referents[p_list] + return list[index] + + @ffi.pyexport("pyobj_t(pyobj_t)") + def pyobj_dup(p): + return store(referents[p]) + + @ffi.pyexport("void(pyobj_t)") + def pyobj_close(p): + discard(p) + + @ffi.pyexport("pyobj_t(pyobj_t, int)") + def pyobj_getitem(p_list, index): + list = referents[p_list] + return store(list[index]) + + @ffi.pyexport("pyobj_t(pyobj_t, pyobj_t)") + def pyobj_add(p1, p2): + return store(referents[p1] + referents[p2]) + + lib = ffi.verify(""" + typedef int pyobj_t; /* an "object descriptor" number */ + + int sum_integers(pyobj_t p_list) { + /* this a demo function written in C, using the API + defined above: length() and getitem(). */ + int i, result = 0; + int count = length(p_list); + for (i=0; i<count; i++) { + int n = getitem(p_list, i); + result += n; + } + return result; + } + + pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial) { + /* same as above, but keeps all additions as Python objects */ + int i; + int count = length(p_list); + pyobj_t p1 = pyobj_dup(p_initial); + for (i=0; i<count; i++) { + pyobj_t p2 = pyobj_getitem(p_list, i); + pyobj_t p3 = pyobj_add(p1, p2); + pyobj_close(p2); + pyobj_close(p1); + p1 = p3; + } + return p1; + } + """) + + with Ref([10, 20, 30, 40]) as p_list: + print lib.sum_integers(p_list) + with Ref(5) as p_initial: + result = discard(lib.sum_objects(p_list, p_initial)) + print result + + assert count_pyobj_alive() == 0 diff --git a/demo/readdir.py b/demo/readdir.py new file mode 100644 index 0000000..b966246 --- /dev/null +++ b/demo/readdir.py @@ -0,0 +1,35 @@ +# A Linux-only demo +# +import sys + +if not sys.platform.startswith('linux'): + raise Exception("Linux-only demo") + +from _readdir import ffi +lib = ffi.dlopen(None) + + +def walk(basefd, path): + print '{', path + dirfd = lib.openat(basefd, path, 0) + if dirfd < 0: + # error in openat() + return + dir = lib.fdopendir(dirfd) + dirent = ffi.new("struct dirent *") + result = ffi.new("struct dirent **") + while True: + if lib.readdir_r(dir, dirent, result): + # error in readdir_r() + break + if result[0] == ffi.NULL: + break + name = ffi.string(dirent.d_name) + print '%3d %s' % (dirent.d_type, name) + if dirent.d_type == 4 and name != '.' and name != '..': + walk(dirfd, name) + lib.closedir(dir) + print '}' + + +walk(-1, "/tmp") diff --git a/demo/readdir2.py b/demo/readdir2.py new file mode 100644 index 0000000..b564b51 --- /dev/null +++ b/demo/readdir2.py @@ -0,0 +1,35 @@ +# A Linux-only demo, using set_source() instead of hard-coding the exact layouts +# +import sys + +if not sys.platform.startswith('linux'): + raise Exception("Linux-only demo") + +# run readdir2_build first, then make sure the shared object is on sys.path +from _readdir2_cffi import ffi, lib + + +def walk(basefd, path): + print '{', path + dirfd = lib.openat(basefd, path, 0) + if dirfd < 0: + # error in openat() + return + dir = lib.fdopendir(dirfd) + dirent = ffi.new("struct dirent *") + result = ffi.new("struct dirent **") + while True: + if lib.readdir_r(dir, dirent, result): + # error in readdir_r() + break + if result[0] == ffi.NULL: + break + name = ffi.string(dirent.d_name) + print '%3d %s' % (dirent.d_type, name) + if dirent.d_type == lib.DT_DIR and name != '.' and name != '..': + walk(dirfd, name) + lib.closedir(dir) + print '}' + + +walk(-1, "/tmp") diff --git a/demo/readdir2_build.py b/demo/readdir2_build.py new file mode 100644 index 0000000..5cfd872 --- /dev/null +++ b/demo/readdir2_build.py @@ -0,0 +1,36 @@ +from cffi import FFI + +ffi = FFI() +ffi.cdef(""" + + typedef ... DIR; + + struct dirent { + unsigned char d_type; /* type of file; not supported + by all file system types */ + char d_name[...]; /* filename */ + ...; + }; + + int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); + int openat(int dirfd, const char *pathname, int flags); + DIR *fdopendir(int fd); + int closedir(DIR *dirp); + + static const int DT_DIR; + +""") +ffi.set_source("_readdir2_cffi", """ +#ifndef _ATFILE_SOURCE +# define _ATFILE_SOURCE +#endif +#ifndef _BSD_SOURCE +# define _BSD_SOURCE +#endif +#include <fcntl.h> +#include <sys/types.h> +#include <dirent.h> +""") + +if __name__ == '__main__': + ffi.compile() diff --git a/demo/readdir2_setup.py b/demo/readdir2_setup.py new file mode 100644 index 0000000..bd8c19f --- /dev/null +++ b/demo/readdir2_setup.py @@ -0,0 +1,9 @@ +from distutils.core import setup +import readdir2_build + +setup( + name="readdir2", + version="0.1", + py_modules=["readdir2"], + ext_modules=[readdir2_build.ffi.distutils_extension('build')], +) diff --git a/demo/readdir_build.py b/demo/readdir_build.py new file mode 100644 index 0000000..f97f404 --- /dev/null +++ b/demo/readdir_build.py @@ -0,0 +1,33 @@ +import sys +from cffi import FFI + +if not sys.platform.startswith('linux'): + raise Exception("Linux-only demo") + + +ffi = FFI() +ffi.cdef(""" + + typedef void DIR; + typedef long ino_t; + typedef long off_t; + + struct dirent { + ino_t d_ino; /* inode number */ + off_t d_off; /* offset to the next dirent */ + unsigned short d_reclen; /* length of this record */ + unsigned char d_type; /* type of file; not supported + by all file system types */ + char d_name[256]; /* filename */ + }; + + int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); + int openat(int dirfd, const char *pathname, int flags); + DIR *fdopendir(int fd); + int closedir(DIR *dirp); + +""") +ffi.set_source("_readdir", None) + +if __name__ == '__main__': + ffi.compile() diff --git a/demo/readdir_ctypes.py b/demo/readdir_ctypes.py new file mode 100644 index 0000000..4fd1d17 --- /dev/null +++ b/demo/readdir_ctypes.py @@ -0,0 +1,69 @@ +# A Linux-only demo +# +# For comparison purposes, this is a ctypes version of readdir.py. +import sys +import ctypes + +if not sys.platform.startswith('linux'): + raise Exception("Linux-only demo") + + +DIR_p = ctypes.c_void_p +ino_t = ctypes.c_long +off_t = ctypes.c_long + +class DIRENT(ctypes.Structure): + _fields_ = [ + ('d_ino', ino_t), # inode number + ('d_off', off_t), # offset to the next dirent + ('d_reclen', ctypes.c_ushort), # length of this record + ('d_type', ctypes.c_ubyte), # type of file; not supported + # by all file system types + ('d_name', ctypes.c_char * 256), # filename + ] +DIRENT_p = ctypes.POINTER(DIRENT) +DIRENT_pp = ctypes.POINTER(DIRENT_p) + +C = ctypes.CDLL(None) + +readdir_r = C.readdir_r +readdir_r.argtypes = [DIR_p, DIRENT_p, DIRENT_pp] +readdir_r.restype = ctypes.c_int + +openat = C.openat +openat.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int] +openat.restype = ctypes.c_int + +fdopendir = C.fdopendir +fdopendir.argtypes = [ctypes.c_int] +fdopendir.restype = DIR_p + +closedir = C.closedir +closedir.argtypes = [DIR_p] +closedir.restype = ctypes.c_int + + +def walk(basefd, path): + print '{', path + dirfd = openat(basefd, path, 0) + if dirfd < 0: + # error in openat() + return + dir = fdopendir(dirfd) + dirent = DIRENT() + result = DIRENT_p() + while True: + if readdir_r(dir, dirent, result): + # error in readdir_r() + break + if not result: + break + name = dirent.d_name + print '%3d %s' % (dirent.d_type, name) + if dirent.d_type == 4 and name != '.' and name != '..': + walk(dirfd, name) + closedir(dir) + print '}' + + +walk(-1, "/tmp") diff --git a/demo/readdir_setup.py b/demo/readdir_setup.py new file mode 100644 index 0000000..c8abdcb --- /dev/null +++ b/demo/readdir_setup.py @@ -0,0 +1,11 @@ +from setuptools import setup + +setup( + name="example", + version="0.1", + py_modules=["readdir"], + setup_requires=["cffi>=1.0.dev0"], + cffi_modules=["readdir_build.py:ffi"], + install_requires=["cffi>=1.0.dev0"], + zip_safe=False, +) diff --git a/demo/recopendirtype.py b/demo/recopendirtype.py new file mode 100644 index 0000000..768318b --- /dev/null +++ b/demo/recopendirtype.py @@ -0,0 +1,50 @@ +from _recopendirtype import ffi, lib + + +def _posix_error(): + raise OSError(ffi.errno, os.strerror(ffi.errno)) + +_dtype_to_smode = { + lib.DT_BLK: 0o060000, + lib.DT_CHR: 0o020000, + lib.DT_DIR: 0o040000, + lib.DT_FIFO: 0o010000, + lib.DT_LNK: 0o120000, + lib.DT_REG: 0o100000, + lib.DT_SOCK: 0o140000, +} + +def opendir(dir): + if len(dir) == 0: + dir = b'.' + dirname = dir + if not dirname.endswith(b'/'): + dirname += b'/' + dirp = lib.opendir(dir) + if dirp == ffi.NULL: + raise _posix_error() + dirent = ffi.new("struct dirent *") + result = ffi.new("struct dirent **") + try: + while True: + ffi.errno = 0 + err = lib.readdir_r(dirp, dirent, result) + if err: # really got an error + raise OSError(err, os.strerror(err)) + if result[0] == ffi.NULL: + return # + name = ffi.string(dirent.d_name) + if name == b'.' or name == b'..': + continue + name = dirname + name + try: + smode = _dtype_to_smode[dirent.d_type] + except KeyError: + smode = os.lstat(name).st_mode + yield name, smode + finally: + lib.closedir(dirp) + +if __name__ == '__main__': + for name, smode in opendir(b'/tmp'): + print(hex(smode), name) diff --git a/demo/recopendirtype_build.py b/demo/recopendirtype_build.py new file mode 100644 index 0000000..fa62a05 --- /dev/null +++ b/demo/recopendirtype_build.py @@ -0,0 +1,19 @@ +from cffi import FFI +import bsdopendirtype_build + +ffi = FFI() + +# ========== This is a demo of ffi.include() ========== +ffi.include(bsdopendirtype_build.ffi) + +ffi.cdef(""" + int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); +""") + +ffi.set_source("_recopendirtype", """ + #include <sys/types.h> + #include <dirent.h> +""") + +if __name__ == '__main__': + ffi.compile() diff --git a/demo/setup_manual.py b/demo/setup_manual.py new file mode 100644 index 0000000..2569bb4 --- /dev/null +++ b/demo/setup_manual.py @@ -0,0 +1,5 @@ +from distutils.core import setup +from distutils.extension import Extension +setup(name='manual', + ext_modules=[Extension(name='manual', + sources=['manual.c'])]) diff --git a/demo/winclipboard.py b/demo/winclipboard.py new file mode 100644 index 0000000..5278cd0 --- /dev/null +++ b/demo/winclipboard.py @@ -0,0 +1,40 @@ +__author__ = "Israel Fruchter <israel.fruchter@gmail.com>" + +import sys, os + +if not sys.platform == 'win32': + raise Exception("Windows-only demo") + +try: + from _winclipboard_cffi import ffi, lib +except ImportError: + print 'run winclipboard_build first, then make sure the shared object is on sys.path' + sys.exit(1) + +# ffi "knows" about the declared variables and functions from the +# cdef parts of the module _winclipboard_cffi created, +# lib "knows" how to call the functions from the set_source parts +# of the module. + +def CopyToClipboard(string): + ''' + use win32 api to copy `string` to the clipboard + ''' + hWnd = lib.GetConsoleWindow() + + if lib.OpenClipboard(hWnd): + cstring = ffi.new("char[]", string) + size = ffi.sizeof(cstring) + + # make it a moveable memory for other processes + hGlobal = lib.GlobalAlloc(lib.GMEM_MOVEABLE, size) + buffer = lib.GlobalLock(hGlobal) + lib.memcpy(buffer, cstring, size) + lib.GlobalUnlock(hGlobal) + + res = lib.EmptyClipboard() + res = lib.SetClipboardData(lib.CF_TEXT, buffer) + + lib.CloseClipboard() + +CopyToClipboard("hello world from cffi") diff --git a/demo/winclipboard_build.py b/demo/winclipboard_build.py new file mode 100644 index 0000000..1a510eb --- /dev/null +++ b/demo/winclipboard_build.py @@ -0,0 +1,36 @@ +from cffi import FFI + +ffi = FFI() +ffi.cdef(''' + typedef void * HANDLE; + typedef HANDLE HWND; + typedef int BOOL; + typedef unsigned int UINT; + typedef int SIZE_T; + typedef char * LPTSTR; + typedef HANDLE HGLOBAL; + typedef HANDLE LPVOID; + + HWND GetConsoleWindow(void); + + LPVOID GlobalLock( HGLOBAL hMem ); + BOOL GlobalUnlock( HGLOBAL hMem ); + HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes); + + BOOL OpenClipboard(HWND hWndNewOwner); + BOOL CloseClipboard(void); + BOOL EmptyClipboard(void); + HANDLE SetClipboardData(UINT uFormat, HANDLE hMem); + + #define CF_TEXT ... + #define GMEM_MOVEABLE ... + + void * memcpy(void * s1, void * s2, int n); + ''') + +ffi.set_source('_winclipboard_cffi', ''' + #include <windows.h> +''', libraries=["user32"]) + +if __name__ == '__main__': + ffi.compile() diff --git a/demo/xclient.py b/demo/xclient.py new file mode 100644 index 0000000..e4b3dd2 --- /dev/null +++ b/demo/xclient.py @@ -0,0 +1,27 @@ +import sys, os + +# run xclient_build first, then make sure the shared object is on sys.path +from _xclient_cffi import ffi, lib + + +# ffi "knows" about the declared variables and functions from the +# cdef parts of the module xclient_build created, +# lib "knows" how to call the functions from the set_source parts +# of the module. + + +class XError(Exception): + pass + +def main(): + display = lib.XOpenDisplay(ffi.NULL) + if display == ffi.NULL: + raise XError("cannot open display") + w = lib.XCreateSimpleWindow(display, lib.DefaultRootWindow(display), + 10, 10, 500, 350, 0, 0, 0) + lib.XMapRaised(display, w) + event = ffi.new("XEvent *") + lib.XNextEvent(display, event) + +if __name__ == '__main__': + main() diff --git a/demo/xclient_build.py b/demo/xclient_build.py new file mode 100644 index 0000000..d6ce9da --- /dev/null +++ b/demo/xclient_build.py @@ -0,0 +1,25 @@ +from cffi import FFI +ffi = FFI() +ffi.cdef(""" + +typedef ... Display; +typedef struct { ...; } Window; + +typedef struct { int type; ...; } XEvent; + +Display *XOpenDisplay(char *display_name); +Window DefaultRootWindow(Display *display); +int XMapRaised(Display *display, Window w); +Window XCreateSimpleWindow(Display *display, Window parent, int x, int y, + unsigned int width, unsigned int height, + unsigned int border_width, unsigned long border, + unsigned long background); +int XNextEvent(Display *display, XEvent *event_return); +""") + +ffi.set_source('_xclient_cffi', """ + #include <X11/Xlib.h> +""", libraries=['X11']) + +if __name__ == '__main__': + ffi.compile(verbose=True) |