diff options
Diffstat (limited to 'Modules/_cursesmodule.c')
-rw-r--r-- | Modules/_cursesmodule.c | 701 |
1 files changed, 586 insertions, 115 deletions
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 5e1afa9..de45f51 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -105,10 +105,6 @@ char *PyCursesVersion = "2.2"; #include "Python.h" -#ifdef __osf__ -#define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ -#endif - #ifdef __hpux #define STRICT_SYSV_CURSES #endif @@ -125,6 +121,10 @@ extern int setupterm(char *,int,int *); #include <term.h> #endif +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif + #if !defined(HAVE_NCURSES_H) && (defined(sgi) || defined(__sun) || defined(SCO5)) #define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ typedef chtype attr_t; /* No attr_t type is available */ @@ -147,6 +147,8 @@ static int initialised = FALSE; /* Tells whether start_color() has been called to initialise color usage. */ static int initialisedcolors = FALSE; +static char *screen_encoding = NULL; + /* Utility Macros */ #define PyCursesSetupTermCalled \ if (initialised_setupterm != TRUE) { \ @@ -179,7 +181,7 @@ static int initialisedcolors = FALSE; */ static PyObject * -PyCursesCheckERR(int code, char *fname) +PyCursesCheckERR(int code, const char *fname) { if (code != ERR) { Py_INCREF(Py_None); @@ -194,25 +196,193 @@ PyCursesCheckERR(int code, char *fname) } } +/* Convert an object to a byte (an integer of type chtype): + + - int + - bytes of length 1 + - str of length 1 + + Return 1 on success, 0 on error (invalid type or integer overflow). */ static int -PyCurses_ConvertToChtype(PyObject *obj, chtype *ch) +PyCurses_ConvertToChtype(PyCursesWindowObject *win, PyObject *obj, chtype *ch) { - if (PyLong_CheckExact(obj)) { - int overflow; - /* XXX should the truncation by the cast also be reported - as an error? */ - *ch = (chtype) PyLong_AsLongAndOverflow(obj, &overflow); - if (overflow) + long value; + if(PyBytes_Check(obj) && PyBytes_Size(obj) == 1) { + value = (unsigned char)PyBytes_AsString(obj)[0]; + } + else if (PyUnicode_Check(obj)) { + if (PyUnicode_GetLength(obj) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); return 0; - } else if(PyBytes_Check(obj) - && (PyBytes_Size(obj) == 1)) { - *ch = (chtype) *PyBytes_AsString(obj); - } else if (PyUnicode_Check(obj) && PyUnicode_GetSize(obj) == 1) { - *ch = (chtype) *PyUnicode_AS_UNICODE(obj); - } else { + } + value = PyUnicode_READ_CHAR(obj, 0); + if (128 < value) { + PyObject *bytes; + const char *encoding; + if (win) + encoding = win->encoding; + else + encoding = screen_encoding; + bytes = PyUnicode_AsEncodedObject(obj, encoding, NULL); + if (bytes == NULL) + return 0; + if (PyBytes_GET_SIZE(bytes) == 1) + value = (unsigned char)PyBytes_AS_STRING(bytes)[0]; + else + value = -1; + Py_DECREF(bytes); + if (value < 0) + goto overflow; + } + } + else if (PyLong_CheckExact(obj)) { + int long_overflow; + value = PyLong_AsLongAndOverflow(obj, &long_overflow); + if (long_overflow) + goto overflow; + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); return 0; } + *ch = (chtype)value; + if ((long)*ch != value) + goto overflow; return 1; + +overflow: + PyErr_SetString(PyExc_OverflowError, + "byte doesn't fit in chtype"); + return 0; +} + +/* Convert an object to a byte (chtype) or a character (cchar_t): + + - int + - bytes of length 1 + - str of length 1 + + Return: + + - 2 if obj is a character (written into *wch) + - 1 if obj is a byte (written into *ch) + - 0 on error: raise an exception */ +static int +PyCurses_ConvertToCchar_t(PyCursesWindowObject *win, PyObject *obj, + chtype *ch +#ifdef HAVE_NCURSESW + , cchar_t *wch +#endif + ) +{ + int ret = 0; + long value; +#ifdef HAVE_NCURSESW + wchar_t buffer[2]; +#endif + + if (PyUnicode_Check(obj)) { +#ifdef HAVE_NCURSESW + if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + memset(wch->chars, 0, sizeof(wch->chars)); + wch->chars[0] = buffer[0]; + return 2; +#else + return PyCurses_ConvertToChtype(win, obj, ch); +#endif + } + else if(PyBytes_Check(obj) && PyBytes_Size(obj) == 1) { + value = (unsigned char)PyBytes_AsString(obj)[0]; + ret = 1; + } + else if (PyLong_CheckExact(obj)) { + int overflow; + value = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, + "int doesn't fit in long"); + return 0; + } +#ifdef HAVE_NCURSESW + ret = 2; +#else + ret = 1; +#endif + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } +#ifdef HAVE_NCURSESW + if (ret == 2) { + memset(wch->chars, 0, sizeof(wch->chars)); + wch->chars[0] = (wchar_t)value; + if ((long)wch->chars[0] != value) { + PyErr_Format(PyExc_OverflowError, + "character doesn't fit in wchar_t"); + return 0; + } + } + else +#endif + { + *ch = (chtype)value; + if ((long)*ch != value) { + PyErr_Format(PyExc_OverflowError, + "byte doesn't fit in chtype"); + return 0; + } + } + return ret; +} + +/* Convert an object to a byte string (char*) or a wide character string + (wchar_t*). Return: + + - 2 if obj is a character string (written into *wch) + - 1 if obj is a byte string (written into *bytes) + - 0 on error: raise an exception */ +static int +PyCurses_ConvertToString(PyCursesWindowObject *win, PyObject *obj, + PyObject **bytes, wchar_t **wstr) +{ + if (PyUnicode_Check(obj)) { +#ifdef HAVE_NCURSESW + assert (wstr != NULL); + *wstr = PyUnicode_AsWideCharString(obj, NULL); + if (*wstr == NULL) + return 0; + return 2; +#else + assert (wstr == NULL); + *bytes = PyUnicode_AsEncodedObject(obj, win->encoding, NULL); + if (*bytes == NULL) + return 0; + return 1; +#endif + } + else if (PyBytes_Check(obj)) { + Py_INCREF(obj); + *bytes = obj; + return 1; + } + + PyErr_Format(PyExc_TypeError, "expect bytes or str, got %s", + Py_TYPE(obj)->tp_name); + return 0; } /* Function versions of the 3 functions for testing whether curses has been @@ -358,13 +528,37 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns") /* Allocation and deallocation of Window Objects */ static PyObject * -PyCursesWindow_New(WINDOW *win) +PyCursesWindow_New(WINDOW *win, const char *encoding) { PyCursesWindowObject *wo; + if (encoding == NULL) { +#if defined(MS_WINDOWS) + char *buffer[100]; + UINT cp; + cp = GetConsoleOutputCP(); + if (cp != 0) { + PyOS_snprintf(buffer, sizeof(buffer), "cp%u", cp); + encoding = buffer; + } +#elif defined(CODESET) + const char *codeset = nl_langinfo(CODESET); + if (codeset != NULL && codeset[0] != 0) + encoding = codeset; +#endif + if (encoding == NULL) + encoding = "utf-8"; + } + wo = PyObject_NEW(PyCursesWindowObject, &PyCursesWindow_Type); if (wo == NULL) return NULL; wo->win = win; + wo->encoding = strdup(encoding); + if (wo->encoding == NULL) { + Py_DECREF(wo); + PyErr_NoMemory(); + return NULL; + } return (PyObject *)wo; } @@ -372,6 +566,8 @@ static void PyCursesWindow_Dealloc(PyCursesWindowObject *wo) { if (wo->win != stdscr) delwin(wo->win); + if (wo->encoding != NULL) + free(wo->encoding); PyObject_DEL(wo); } @@ -381,29 +577,34 @@ static PyObject * PyCursesWindow_AddCh(PyCursesWindowObject *self, PyObject *args) { int rtn, x, y, use_xy = FALSE; - PyObject *temp; - chtype ch = 0; + PyObject *chobj; + int type; + chtype ch; +#ifdef HAVE_NCURSESW + cchar_t wch; +#endif attr_t attr = A_NORMAL; long lattr; + const char *funcname; switch (PyTuple_Size(args)) { case 1: - if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + if (!PyArg_ParseTuple(args, "O;ch or int", &chobj)) return NULL; break; case 2: - if (!PyArg_ParseTuple(args, "Ol;ch or int,attr", &temp, &lattr)) + if (!PyArg_ParseTuple(args, "Ol;ch or int,attr", &chobj, &lattr)) return NULL; attr = lattr; break; case 3: - if (!PyArg_ParseTuple(args,"iiO;y,x,ch or int", &y, &x, &temp)) + if (!PyArg_ParseTuple(args,"iiO;y,x,ch or int", &y, &x, &chobj)) return NULL; use_xy = TRUE; break; case 4: if (!PyArg_ParseTuple(args,"iiOl;y,x,ch or int, attr", - &y, &x, &temp, &lattr)) + &y, &x, &chobj, &lattr)) return NULL; attr = lattr; use_xy = TRUE; @@ -413,17 +614,33 @@ PyCursesWindow_AddCh(PyCursesWindowObject *self, PyObject *args) return NULL; } - if (!PyCurses_ConvertToChtype(temp, &ch)) { - PyErr_SetString(PyExc_TypeError, "argument 1 or 3 must be a ch or an int"); - return NULL; +#ifdef HAVE_NCURSESW + type = PyCurses_ConvertToCchar_t(self, chobj, &ch, &wch); + if (type == 2) { + funcname = "add_wch"; + wch.attr = attr; + if (use_xy == TRUE) + rtn = mvwadd_wch(self->win,y,x, &wch); + else { + rtn = wadd_wch(self->win, &wch); + } + } + else +#else + type = PyCurses_ConvertToCchar_t(self, chobj, &ch); +#endif + if (type == 1) { + funcname = "addch"; + if (use_xy == TRUE) + rtn = mvwaddch(self->win,y,x, ch | attr); + else { + rtn = waddch(self->win, ch | attr); + } } - - if (use_xy == TRUE) - rtn = mvwaddch(self->win,y,x, ch | attr); else { - rtn = waddch(self->win, ch | attr); + return NULL; } - return PyCursesCheckERR(rtn, "addch"); + return PyCursesCheckERR(rtn, funcname); } static PyObject * @@ -431,29 +648,34 @@ PyCursesWindow_AddStr(PyCursesWindowObject *self, PyObject *args) { int rtn; int x, y; - char *str; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif attr_t attr = A_NORMAL , attr_old = A_NORMAL; long lattr; int use_xy = FALSE, use_attr = FALSE; + const char *funcname; switch (PyTuple_Size(args)) { case 1: - if (!PyArg_ParseTuple(args,"s;str", &str)) + if (!PyArg_ParseTuple(args,"O;str", &strobj)) return NULL; break; case 2: - if (!PyArg_ParseTuple(args,"sl;str,attr", &str, &lattr)) + if (!PyArg_ParseTuple(args,"Ol;str,attr", &strobj, &lattr)) return NULL; attr = lattr; use_attr = TRUE; break; case 3: - if (!PyArg_ParseTuple(args,"iis;int,int,str", &y, &x, &str)) + if (!PyArg_ParseTuple(args,"iiO;int,int,str", &y, &x, &strobj)) return NULL; use_xy = TRUE; break; case 4: - if (!PyArg_ParseTuple(args,"iisl;int,int,str,attr", &y, &x, &str, &lattr)) + if (!PyArg_ParseTuple(args,"iiOl;int,int,str,attr", &y, &x, &strobj, &lattr)) return NULL; attr = lattr; use_xy = use_attr = TRUE; @@ -462,47 +684,74 @@ PyCursesWindow_AddStr(PyCursesWindowObject *self, PyObject *args) PyErr_SetString(PyExc_TypeError, "addstr requires 1 to 4 arguments"); return NULL; } - +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; if (use_attr == TRUE) { attr_old = getattrs(self->win); (void)wattrset(self->win,attr); } - if (use_xy == TRUE) - rtn = mvwaddstr(self->win,y,x,str); +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "addwstr"; + if (use_xy == TRUE) + rtn = mvwaddwstr(self->win,y,x,wstr); + else + rtn = waddwstr(self->win,wstr); + PyMem_Free(wstr); + } else - rtn = waddstr(self->win,str); +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "addstr"; + if (use_xy == TRUE) + rtn = mvwaddstr(self->win,y,x,str); + else + rtn = waddstr(self->win,str); + Py_DECREF(bytesobj); + } if (use_attr == TRUE) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, "addstr"); + return PyCursesCheckERR(rtn, funcname); } static PyObject * PyCursesWindow_AddNStr(PyCursesWindowObject *self, PyObject *args) { int rtn, x, y, n; - char *str; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif attr_t attr = A_NORMAL , attr_old = A_NORMAL; long lattr; int use_xy = FALSE, use_attr = FALSE; + const char *funcname; switch (PyTuple_Size(args)) { case 2: - if (!PyArg_ParseTuple(args,"si;str,n", &str, &n)) + if (!PyArg_ParseTuple(args,"Oi;str,n", &strobj, &n)) return NULL; break; case 3: - if (!PyArg_ParseTuple(args,"sil;str,n,attr", &str, &n, &lattr)) + if (!PyArg_ParseTuple(args,"Oil;str,n,attr", &strobj, &n, &lattr)) return NULL; attr = lattr; use_attr = TRUE; break; case 4: - if (!PyArg_ParseTuple(args,"iisi;y,x,str,n", &y, &x, &str, &n)) + if (!PyArg_ParseTuple(args,"iiOi;y,x,str,n", &y, &x, &strobj, &n)) return NULL; use_xy = TRUE; break; case 5: - if (!PyArg_ParseTuple(args,"iisil;y,x,str,n,attr", &y, &x, &str, &n, &lattr)) + if (!PyArg_ParseTuple(args,"iiOil;y,x,str,n,attr", &y, &x, &strobj, &n, &lattr)) return NULL; attr = lattr; use_xy = use_attr = TRUE; @@ -511,18 +760,41 @@ PyCursesWindow_AddNStr(PyCursesWindowObject *self, PyObject *args) PyErr_SetString(PyExc_TypeError, "addnstr requires 2 to 5 arguments"); return NULL; } +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; if (use_attr == TRUE) { attr_old = getattrs(self->win); (void)wattrset(self->win,attr); } - if (use_xy == TRUE) - rtn = mvwaddnstr(self->win,y,x,str,n); +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "addnwstr"; + if (use_xy == TRUE) + rtn = mvwaddnwstr(self->win,y,x,wstr,n); + else + rtn = waddnwstr(self->win,wstr,n); + PyMem_Free(wstr); + } else - rtn = waddnstr(self->win,str,n); +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "addnstr"; + if (use_xy == TRUE) + rtn = mvwaddnstr(self->win,y,x,str,n); + else + rtn = waddnstr(self->win,str,n); + Py_DECREF(bytesobj); + } if (use_attr == TRUE) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, "addnstr"); + return PyCursesCheckERR(rtn, funcname); } static PyObject * @@ -548,10 +820,8 @@ PyCursesWindow_Bkgd(PyCursesWindowObject *self, PyObject *args) return NULL; } - if (!PyCurses_ConvertToChtype(temp, &bkgd)) { - PyErr_SetString(PyExc_TypeError, "argument 1 or 3 must be a ch or an int"); + if (!PyCurses_ConvertToChtype(self, temp, &bkgd)) return NULL; - } return PyCursesCheckERR(wbkgd(self->win, bkgd | attr), "bkgd"); } @@ -606,10 +876,8 @@ PyCursesWindow_BkgdSet(PyCursesWindowObject *self, PyObject *args) return NULL; } - if (!PyCurses_ConvertToChtype(temp, &bkgd)) { - PyErr_SetString(PyExc_TypeError, "argument 1 must be a ch or an int"); + if (!PyCurses_ConvertToChtype(self, temp, &bkgd)) return NULL; - } wbkgdset(self->win, bkgd | attr); return PyCursesCheckERR(0, "bkgdset"); @@ -634,11 +902,8 @@ PyCursesWindow_Border(PyCursesWindowObject *self, PyObject *args) return NULL; for(i=0; i<8; i++) { - if (temp[i] != NULL && !PyCurses_ConvertToChtype(temp[i], &ch[i])) { - PyErr_Format(PyExc_TypeError, - "argument %i must be a ch or an int", i+1); + if (temp[i] != NULL && !PyCurses_ConvertToChtype(self, temp[i], &ch[i])) return NULL; - } } wborder(self->win, @@ -783,7 +1048,7 @@ PyCursesWindow_DerWin(PyCursesWindowObject *self, PyObject *args) return NULL; } - return (PyObject *)PyCursesWindow_New(win); + return (PyObject *)PyCursesWindow_New(win, NULL); } static PyObject * @@ -811,10 +1076,8 @@ PyCursesWindow_EchoChar(PyCursesWindowObject *self, PyObject *args) return NULL; } - if (!PyCurses_ConvertToChtype(temp, &ch)) { - PyErr_SetString(PyExc_TypeError, "argument 1 must be a ch or an int"); + if (!PyCurses_ConvertToChtype(self, temp, &ch)) return NULL; - } #ifdef WINDOW_HAS_FLAGS if (self->win->_flags & _ISPAD) @@ -910,6 +1173,40 @@ PyCursesWindow_GetKey(PyCursesWindowObject *self, PyObject *args) } } +#ifdef HAVE_NCURSESW +static PyObject * +PyCursesWindow_Get_WCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int ct; + wint_t rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + ct = wget_wch(self->win,&rtn); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + ct = mvwget_wch(self->win,y,x,&rtn); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "get_wch requires 0 or 2 arguments"); + return NULL; + } + if (ct == ERR) { + /* get_wch() returns ERR in nodelay mode */ + PyErr_SetString(PyCursesError, "no input"); + return NULL; + } + return PyLong_FromLong(rtn); +} +#endif + static PyObject * PyCursesWindow_GetStr(PyCursesWindowObject *self, PyObject *args) { @@ -1001,11 +1298,8 @@ PyCursesWindow_Hline(PyCursesWindowObject *self, PyObject *args) } if (code != ERR) { - if (!PyCurses_ConvertToChtype(temp, &ch)) { - PyErr_SetString(PyExc_TypeError, - "argument 1 or 3 must be a ch or an int"); + if (!PyCurses_ConvertToChtype(self, temp, &ch)) return NULL; - } return PyCursesCheckERR(whline(self->win, ch | attr, n), "hline"); } else return PyCursesCheckERR(code, "wmove"); @@ -1046,11 +1340,8 @@ PyCursesWindow_InsCh(PyCursesWindowObject *self, PyObject *args) return NULL; } - if (!PyCurses_ConvertToChtype(temp, &ch)) { - PyErr_SetString(PyExc_TypeError, - "argument 1 or 3 must be a ch or an int"); + if (!PyCurses_ConvertToChtype(self, temp, &ch)) return NULL; - } if (use_xy == TRUE) rtn = mvwinsch(self->win,y,x, ch | attr); @@ -1121,29 +1412,34 @@ PyCursesWindow_InsStr(PyCursesWindowObject *self, PyObject *args) { int rtn; int x, y; - char *str; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif attr_t attr = A_NORMAL , attr_old = A_NORMAL; long lattr; int use_xy = FALSE, use_attr = FALSE; + const char *funcname; switch (PyTuple_Size(args)) { case 1: - if (!PyArg_ParseTuple(args,"s;str", &str)) + if (!PyArg_ParseTuple(args,"O;str", &strobj)) return NULL; break; case 2: - if (!PyArg_ParseTuple(args,"sl;str,attr", &str, &lattr)) + if (!PyArg_ParseTuple(args,"Ol;str,attr", &strobj, &lattr)) return NULL; attr = lattr; use_attr = TRUE; break; case 3: - if (!PyArg_ParseTuple(args,"iis;y,x,str", &y, &x, &str)) + if (!PyArg_ParseTuple(args,"iiO;y,x,str", &y, &x, &strobj)) return NULL; use_xy = TRUE; break; case 4: - if (!PyArg_ParseTuple(args,"iisl;y,x,str,attr", &y, &x, &str, &lattr)) + if (!PyArg_ParseTuple(args,"iiOl;y,x,str,attr", &y, &x, &strobj, &lattr)) return NULL; attr = lattr; use_xy = use_attr = TRUE; @@ -1153,46 +1449,75 @@ PyCursesWindow_InsStr(PyCursesWindowObject *self, PyObject *args) return NULL; } +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + if (use_attr == TRUE) { attr_old = getattrs(self->win); (void)wattrset(self->win,attr); } - if (use_xy == TRUE) - rtn = mvwinsstr(self->win,y,x,str); +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "inswstr"; + if (use_xy == TRUE) + rtn = mvwins_wstr(self->win,y,x,wstr); + else + rtn = wins_wstr(self->win,wstr); + PyMem_Free(wstr); + } else - rtn = winsstr(self->win,str); +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "insstr"; + if (use_xy == TRUE) + rtn = mvwinsstr(self->win,y,x,str); + else + rtn = winsstr(self->win,str); + Py_DECREF(bytesobj); + } if (use_attr == TRUE) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, "insstr"); + return PyCursesCheckERR(rtn, funcname); } static PyObject * PyCursesWindow_InsNStr(PyCursesWindowObject *self, PyObject *args) { int rtn, x, y, n; - char *str; + int strtype; + PyObject *strobj, *bytesobj = NULL; +#ifdef HAVE_NCURSESW + wchar_t *wstr = NULL; +#endif attr_t attr = A_NORMAL , attr_old = A_NORMAL; long lattr; int use_xy = FALSE, use_attr = FALSE; + const char *funcname; switch (PyTuple_Size(args)) { case 2: - if (!PyArg_ParseTuple(args,"si;str,n", &str, &n)) + if (!PyArg_ParseTuple(args,"Oi;str,n", &strobj, &n)) return NULL; break; case 3: - if (!PyArg_ParseTuple(args,"sil;str,n,attr", &str, &n, &lattr)) + if (!PyArg_ParseTuple(args,"Oil;str,n,attr", &strobj, &n, &lattr)) return NULL; attr = lattr; use_attr = TRUE; break; case 4: - if (!PyArg_ParseTuple(args,"iisi;y,x,str,n", &y, &x, &str, &n)) + if (!PyArg_ParseTuple(args,"iiOi;y,x,str,n", &y, &x, &strobj, &n)) return NULL; use_xy = TRUE; break; case 5: - if (!PyArg_ParseTuple(args,"iisil;y,x,str,n,attr", &y, &x, &str, &n, &lattr)) + if (!PyArg_ParseTuple(args,"iiOil;y,x,str,n,attr", &y, &x, &strobj, &n, &lattr)) return NULL; attr = lattr; use_xy = use_attr = TRUE; @@ -1202,17 +1527,41 @@ PyCursesWindow_InsNStr(PyCursesWindowObject *self, PyObject *args) return NULL; } +#ifdef HAVE_NCURSESW + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, &wstr); +#else + strtype = PyCurses_ConvertToString(self, strobj, &bytesobj, NULL); +#endif + if (strtype == 0) + return NULL; + if (use_attr == TRUE) { attr_old = getattrs(self->win); (void)wattrset(self->win,attr); } - if (use_xy == TRUE) - rtn = mvwinsnstr(self->win,y,x,str,n); +#ifdef HAVE_NCURSESW + if (strtype == 2) { + funcname = "insn_wstr"; + if (use_xy == TRUE) + rtn = mvwins_nwstr(self->win,y,x,wstr,n); + else + rtn = wins_nwstr(self->win,wstr,n); + PyMem_Free(wstr); + } else - rtn = winsnstr(self->win,str,n); +#endif + { + char *str = PyBytes_AS_STRING(bytesobj); + funcname = "insnstr"; + if (use_xy == TRUE) + rtn = mvwinsnstr(self->win,y,x,str,n); + else + rtn = winsnstr(self->win,str,n); + Py_DECREF(bytesobj); + } if (use_attr == TRUE) (void)wattrset(self->win,attr_old); - return PyCursesCheckERR(rtn, "insnstr"); + return PyCursesCheckERR(rtn, funcname); } static PyObject * @@ -1385,10 +1734,12 @@ PyCursesWindow_PutWin(PyCursesWindowObject *self, PyObject *stream) while (1) { char buf[BUFSIZ]; Py_ssize_t n = fread(buf, 1, BUFSIZ, fp); + _Py_IDENTIFIER(write); + if (n <= 0) break; Py_DECREF(res); - res = PyObject_CallMethod(stream, "write", "y#", buf, n); + res = _PyObject_CallMethodId(stream, &PyId_write, "y#", buf, n); if (res == NULL) break; } @@ -1493,7 +1844,7 @@ PyCursesWindow_SubWin(PyCursesWindowObject *self, PyObject *args) return NULL; } - return (PyObject *)PyCursesWindow_New(win); + return (PyObject *)PyCursesWindow_New(win, self->encoding); } static PyObject * @@ -1569,16 +1920,51 @@ PyCursesWindow_Vline(PyCursesWindowObject *self, PyObject *args) } if (code != ERR) { - if (!PyCurses_ConvertToChtype(temp, &ch)) { - PyErr_SetString(PyExc_TypeError, - "argument 1 or 3 must be a ch or an int"); + if (!PyCurses_ConvertToChtype(self, temp, &ch)) return NULL; - } return PyCursesCheckERR(wvline(self->win, ch | attr, n), "vline"); } else return PyCursesCheckERR(code, "wmove"); } +static PyObject * +PyCursesWindow_get_encoding(PyCursesWindowObject *self, void *closure) +{ + return PyUnicode_FromString(self->encoding); +} + +static int +PyCursesWindow_set_encoding(PyCursesWindowObject *self, PyObject *value) +{ + PyObject *ascii; + char *encoding; + + /* It is illegal to del win.encoding */ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "encoding may not be deleted"); + return -1; + } + + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "setting encoding to a non-string"); + return -1; + } + ascii = PyUnicode_AsASCIIString(value); + if (ascii == NULL) + return -1; + encoding = strdup(PyBytes_AS_STRING(ascii)); + if (encoding == NULL) { + PyErr_NoMemory(); + return -1; + } + free(self->encoding); + self->encoding = encoding; + return 0; +} + + static PyMethodDef PyCursesWindow_Methods[] = { {"addch", (PyCFunction)PyCursesWindow_AddCh, METH_VARARGS}, {"addnstr", (PyCFunction)PyCursesWindow_AddNStr, METH_VARARGS}, @@ -1608,6 +1994,9 @@ static PyMethodDef PyCursesWindow_Methods[] = { {"getbkgd", (PyCFunction)PyCursesWindow_GetBkgd, METH_NOARGS}, {"getch", (PyCFunction)PyCursesWindow_GetCh, METH_VARARGS}, {"getkey", (PyCFunction)PyCursesWindow_GetKey, METH_VARARGS}, +#ifdef HAVE_NCURSESW + {"get_wch", (PyCFunction)PyCursesWindow_Get_WCh, METH_VARARGS}, +#endif {"getmaxyx", (PyCFunction)PyCursesWindow_getmaxyx, METH_NOARGS}, {"getparyx", (PyCFunction)PyCursesWindow_getparyx, METH_NOARGS}, {"getstr", (PyCFunction)PyCursesWindow_GetStr, METH_VARARGS}, @@ -1663,6 +2052,14 @@ static PyMethodDef PyCursesWindow_Methods[] = { {NULL, NULL} /* sentinel */ }; +static PyGetSetDef PyCursesWindow_getsets[] = { + {"encoding", + (getter)PyCursesWindow_get_encoding, + (setter)PyCursesWindow_set_encoding, + "the typecode character used to create the array"}, + {NULL, NULL, NULL, NULL } /* sentinel */ +}; + /* -------------------------------------------------------*/ PyTypeObject PyCursesWindow_Type = { @@ -1695,6 +2092,8 @@ PyTypeObject PyCursesWindow_Type = { 0, /*tp_iter*/ 0, /*tp_iternext*/ PyCursesWindow_Methods, /*tp_methods*/ + 0, /* tp_members */ + PyCursesWindow_getsets, /* tp_getset */ }; /********************************************************************* @@ -1875,6 +2274,7 @@ PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream) WINDOW *win; PyCursesInitialised; + _Py_IDENTIFIER(read); strcpy(fn, "/tmp/py.curses.getwin.XXXXXX"); fd = mkstemp(fn); @@ -1886,7 +2286,7 @@ PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream) remove(fn); return PyErr_SetFromErrnoWithFilename(PyExc_IOError, fn); } - data = PyObject_CallMethod(stream, "read", ""); + data = _PyObject_CallMethodId(stream, &PyId_read, ""); if (data == NULL) { fclose(fp); remove(fn); @@ -1917,7 +2317,7 @@ PyCurses_GetWin(PyCursesWindowObject *self, PyObject *stream) PyErr_SetString(PyCursesError, catchall_NULL); return NULL; } - return PyCursesWindow_New(win); + return PyCursesWindow_New(win, NULL); } static PyObject * @@ -1995,10 +2395,11 @@ static PyObject * PyCurses_InitScr(PyObject *self) { WINDOW *win; + PyCursesWindowObject *winobj; if (initialised == TRUE) { wrefresh(stdscr); - return (PyObject *)PyCursesWindow_New(stdscr); + return (PyObject *)PyCursesWindow_New(stdscr, NULL); } win = initscr(); @@ -2090,7 +2491,9 @@ PyCurses_InitScr(PyObject *self) SetDictInt("LINES", LINES); SetDictInt("COLS", COLS); - return (PyObject *)PyCursesWindow_New(win); + winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL); + screen_encoding = winobj->encoding; + return (PyObject *)winobj; } static PyObject * @@ -2292,7 +2695,7 @@ PyCurses_NewPad(PyObject *self, PyObject *args) return NULL; } - return (PyObject *)PyCursesWindow_New(win); + return (PyObject *)PyCursesWindow_New(win, NULL); } static PyObject * @@ -2324,7 +2727,7 @@ PyCurses_NewWindow(PyObject *self, PyObject *args) return NULL; } - return (PyObject *)PyCursesWindow_New(win); + return (PyObject *)PyCursesWindow_New(win, NULL); } static PyObject * @@ -2416,6 +2819,8 @@ update_lines_cols(void) { PyObject *o; PyObject *m = PyImport_ImportModuleNoBlock("curses"); + _Py_IDENTIFIER(LINES); + _Py_IDENTIFIER(COLS); if (!m) return 0; @@ -2425,12 +2830,13 @@ update_lines_cols(void) Py_DECREF(m); return 0; } - if (PyObject_SetAttrString(m, "LINES", o)) { + if (_PyObject_SetAttrId(m, &PyId_LINES, o)) { Py_DECREF(m); Py_DECREF(o); return 0; } - if (PyDict_SetItemString(ModDict, "LINES", o)) { + /* PyId_LINES.object will be initialized here. */ + if (PyDict_SetItem(ModDict, PyId_LINES.object, o)) { Py_DECREF(m); Py_DECREF(o); return 0; @@ -2441,12 +2847,12 @@ update_lines_cols(void) Py_DECREF(m); return 0; } - if (PyObject_SetAttrString(m, "COLS", o)) { + if (_PyObject_SetAttrId(m, &PyId_COLS, o)) { Py_DECREF(m); Py_DECREF(o); return 0; } - if (PyDict_SetItemString(ModDict, "COLS", o)) { + if (PyDict_SetItem(ModDict, PyId_COLS.object, o)) { Py_DECREF(m); Py_DECREF(o); return 0; @@ -2638,10 +3044,8 @@ PyCurses_UnCtrl(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) return NULL; - if (!PyCurses_ConvertToChtype(temp, &ch)) { - PyErr_SetString(PyExc_TypeError, "argument must be a ch or an int"); + if (!PyCurses_ConvertToChtype(NULL, temp, &ch)) return NULL; - } return PyBytes_FromString(unctrl(ch)); } @@ -2654,16 +3058,80 @@ PyCurses_UngetCh(PyObject *self, PyObject *args) PyCursesInitialised; - if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) return NULL; + if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) + return NULL; - if (!PyCurses_ConvertToChtype(temp, &ch)) { - PyErr_SetString(PyExc_TypeError, "argument must be a ch or an int"); + if (!PyCurses_ConvertToChtype(NULL, temp, &ch)) return NULL; - } return PyCursesCheckERR(ungetch(ch), "ungetch"); } +#ifdef HAVE_NCURSESW +/* Convert an object to a character (wchar_t): + + - int + - str of length 1 + + Return 1 on success, 0 on error. */ +static int +PyCurses_ConvertToWchar_t(PyObject *obj, + wchar_t *wch) +{ + if (PyUnicode_Check(obj)) { + wchar_t buffer[2]; + if (PyUnicode_AsWideChar(obj, buffer, 2) != 1) { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, " + "got a str of length %zi", + PyUnicode_GET_LENGTH(obj)); + return 0; + } + *wch = buffer[0]; + return 2; + } + else if (PyLong_CheckExact(obj)) { + long value; + int overflow; + value = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, + "int doesn't fit in long"); + return 0; + } + *wch = (wchar_t)value; + if ((long)*wch != value) { + PyErr_Format(PyExc_OverflowError, + "character doesn't fit in wchar_t"); + return 0; + } + return 1; + } + else { + PyErr_Format(PyExc_TypeError, + "expect bytes or str of length 1, or int, got %s", + Py_TYPE(obj)->tp_name); + return 0; + } +} + +static PyObject * +PyCurses_Unget_Wch(PyObject *self, PyObject *args) +{ + PyObject *obj; + wchar_t wch; + + PyCursesInitialised; + + if (!PyArg_ParseTuple(args,"O", &obj)) + return NULL; + + if (!PyCurses_ConvertToWchar_t(obj, &wch)) + return NULL; + return PyCursesCheckERR(unget_wch(wch), "unget_wch"); +} +#endif + static PyObject * PyCurses_Use_Env(PyObject *self, PyObject *args) { @@ -2791,6 +3259,9 @@ static PyMethodDef PyCurses_methods[] = { {"typeahead", (PyCFunction)PyCurses_TypeAhead, METH_VARARGS}, {"unctrl", (PyCFunction)PyCurses_UnCtrl, METH_VARARGS}, {"ungetch", (PyCFunction)PyCurses_UngetCh, METH_VARARGS}, +#ifdef HAVE_NCURSESW + {"unget_wch", (PyCFunction)PyCurses_Unget_Wch, METH_VARARGS}, +#endif {"use_env", (PyCFunction)PyCurses_Use_Env, METH_VARARGS}, #ifndef STRICT_SYSV_CURSES {"use_default_colors", (PyCFunction)PyCurses_Use_Default_Colors, METH_NOARGS}, |