Issue #12567: The curses module uses Unicode functions for Unicode arguments
when it is linked to the ncurses library. It encodes also Unicode strings to
the locale encoding instead of UTF-8.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index cfa5b7a..166a93a 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -121,6 +121,10 @@
 #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 */
@@ -143,6 +147,8 @@
 /* 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) {                                \
@@ -175,7 +181,7 @@
  */
 
 static PyObject *
-PyCursesCheckERR(int code, char *fname)
+PyCursesCheckERR(int code, const char *fname)
 {
     if (code != ERR) {
         Py_INCREF(Py_None);
@@ -190,28 +196,193 @@
     }
 }
 
+/* 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_GET_LENGTH(obj) == 1) {
-        Py_UCS4 ucs = PyUnicode_READ(PyUnicode_KIND(obj),
-                                     PyUnicode_DATA(obj),
-                                     0);
-        *ch = (chtype)ucs;
-    } 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 || value < 0 || value > 255) {
+            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
@@ -357,13 +528,37 @@
 /* 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;
 }
 
@@ -371,6 +566,8 @@
 PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
 {
     if (wo->win != stdscr) delwin(wo->win);
+    if (wo->encoding != NULL)
+        free(wo->encoding);
     PyObject_DEL(wo);
 }
 
@@ -380,29 +577,34 @@
 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;
@@ -412,17 +614,33 @@
         return NULL;
     }
 
-    if (!PyCurses_ConvertToChtype(temp, &ch)) {
-        PyErr_SetString(PyExc_TypeError, "argument 1 or 3 must be a ch or an int");
+#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);
+        }
+    }
+    else {
         return NULL;
     }
-
-    if (use_xy == TRUE)
-        rtn = mvwaddch(self->win,y,x, ch | attr);
-    else {
-        rtn = waddch(self->win, ch | attr);
-    }
-    return PyCursesCheckERR(rtn, "addch");
+    return PyCursesCheckERR(rtn, funcname);
 }
 
 static PyObject *
@@ -430,29 +648,34 @@
 {
     int rtn;
     int x, y;
-    char *str;
+    int strtype;
+    PyObject *strobj, *bytesobj;
+#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;
@@ -461,47 +684,74 @@
         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;
+#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;
@@ -510,18 +760,41 @@
         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 *
@@ -547,10 +820,8 @@
         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");
 }
@@ -605,10 +876,8 @@
         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");
@@ -633,11 +902,8 @@
         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,
@@ -782,7 +1048,7 @@
         return NULL;
     }
 
-    return (PyObject *)PyCursesWindow_New(win);
+    return (PyObject *)PyCursesWindow_New(win, NULL);
 }
 
 static PyObject *
@@ -810,10 +1076,8 @@
         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)
@@ -1034,11 +1298,8 @@
     }
 
     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");
@@ -1079,11 +1340,8 @@
         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);
@@ -1154,29 +1412,34 @@
 {
     int rtn;
     int x, y;
-    char *str;
+    int strtype;
+    PyObject *strobj, *bytesobj;
+#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;
@@ -1186,46 +1449,75 @@
         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;
+#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;
@@ -1235,17 +1527,41 @@
         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 *
@@ -1528,7 +1844,7 @@
         return NULL;
     }
 
-    return (PyObject *)PyCursesWindow_New(win);
+    return (PyObject *)PyCursesWindow_New(win, self->encoding);
 }
 
 static PyObject *
@@ -1604,16 +1920,51 @@
     }
 
     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},
@@ -1701,6 +2052,13 @@
     {NULL,                  NULL}   /* sentinel */
 };
 
+static PyGetSetDef PyCursesWindow_getsets[] = {
+    {"encoding",
+     (getter)PyCursesWindow_get_encoding,
+     (setter)PyCursesWindow_set_encoding,
+     "the typecode character used to create the array"}
+};
+
 /* -------------------------------------------------------*/
 
 PyTypeObject PyCursesWindow_Type = {
@@ -1733,6 +2091,8 @@
     0,                          /*tp_iter*/
     0,                          /*tp_iternext*/
     PyCursesWindow_Methods,     /*tp_methods*/
+    0,                          /* tp_members */
+    PyCursesWindow_getsets,     /* tp_getset */
 };
 
 /*********************************************************************
@@ -1956,7 +2316,7 @@
         PyErr_SetString(PyCursesError, catchall_NULL);
         return NULL;
     }
-    return PyCursesWindow_New(win);
+    return PyCursesWindow_New(win, NULL);
 }
 
 static PyObject *
@@ -2034,10 +2394,11 @@
 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();
@@ -2129,7 +2490,9 @@
     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 *
@@ -2331,7 +2694,7 @@
         return NULL;
     }
 
-    return (PyObject *)PyCursesWindow_New(win);
+    return (PyObject *)PyCursesWindow_New(win, NULL);
 }
 
 static PyObject *
@@ -2363,7 +2726,7 @@
         return NULL;
     }
 
-    return (PyObject *)PyCursesWindow_New(win);
+    return (PyObject *)PyCursesWindow_New(win, NULL);
 }
 
 static PyObject *
@@ -2680,10 +3043,8 @@
 
     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));
 }
@@ -2696,12 +3057,11 @@
 
     PyCursesInitialised;
 
-    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 (!PyArg_ParseTuple(args,"O;ch or int",&temp))
         return NULL;
-    }
+
+    if (!PyCurses_ConvertToChtype(NULL, temp, &ch))
+        return NULL;
 
     return PyCursesCheckERR(ungetch(ch), "ungetch");
 }