PyObject *error; // curses exception type
PyTypeObject *window_type; // exposed by PyCursesWindow_Type
PyTypeObject *screen_type; // _curses.screen
-#ifdef HAVE_NCURSESW
PyTypeObject *complexchar_type; // _curses.complexchar
PyTypeObject *complexstr_type; // _curses.complexstr
-#endif
PyObject *topscreen; // owned ref to the current screen object,
// or NULL for the initscr() screen
} cursesmodule_state;
return 0;
}
+/* A styled character cell: a cchar_t on a wide build, a chtype on a narrow one
+ (a single locale character ORed with its attributes and color pair).
+ complexchar wraps one cell, complexstr an array. */
#ifdef HAVE_NCURSESW
+typedef cchar_t curses_cell_t;
+#else
+typedef chtype curses_cell_t;
+#endif
+
typedef struct {
PyObject_HEAD
- cchar_t cval;
+ curses_cell_t cval;
} PyCursesComplexCharObject;
#define _PyCursesComplexCharObject_CAST(op) ((PyCursesComplexCharObject *)(op))
-/* An immutable packed array of cchar_t cells -- the "complex character
- string" counterpart of complexchar (as str is to a single character).
- It owns the contiguous buffer that win_wchnstr() fills directly, so a read
- and a re-write is a zero-copy round-trip. */
+/* An immutable inline array of cells -- the string counterpart of complexchar.
+ The read functions fill the buffer directly, so read-then-rewrite is a
+ zero-copy round-trip. */
typedef struct {
PyObject_VAR_HEAD
- cchar_t cells[1]; // ob_size cells, stored inline (variable-size object)
+ curses_cell_t cells[1]; // ob_size cells, stored inline (variable-size object)
} PyCursesComplexStrObject;
#define _PyCursesComplexStrObject_CAST(op) ((PyCursesComplexStrObject *)(op))
/* Build a single character cell from obj.
- Return 1 and store a chtype in *pch for an int or bytes, 2 and store a
- cchar_t (with *attr* applied) in *pwc for a str (a spacing character
- optionally followed by combining characters), or 0 with an exception set.
+ On a wide build, return 1 and store a chtype in *pch for an int or bytes, or
+ 2 and store a cchar_t (with *attr* applied) in *pwc for a str (a spacing
+ character optionally followed by combining characters). On a narrow build
+ there is no cchar_t, so always return 1 with a chtype in *pch. Return 0
+ with an exception set on error.
obj may also be a complexchar, whose cell is used directly; it carries its
own rendition, so supplying *attr* too (attr_given) is rejected. */
static int
PyCurses_ConvertToCell(PyCursesWindowObject *win, PyObject *obj, attr_t attr,
int attr_given, const char *funcname,
- chtype *pch, cchar_t *pwc)
+ chtype *pch
+#ifdef HAVE_NCURSESW
+ , cchar_t *pwc
+#endif
+ )
{
cursesmodule_state *state = get_cursesmodule_state_by_win(win);
if (Py_IS_TYPE(obj, state->complexchar_type)) {
"a complexchar", funcname);
return 0;
}
+#ifdef HAVE_NCURSESW
*pwc = _PyCursesComplexCharObject_CAST(obj)->cval;
return 2;
+#else
+ *pch = _PyCursesComplexCharObject_CAST(obj)->cval;
+ return 1;
+#endif
}
+#ifdef HAVE_NCURSESW
wchar_t wstr[CCHARW_MAX + 1];
int type = PyCurses_ConvertToCchar_t(win, obj, pch, wstr);
if (type == 2) {
}
}
return type;
+#else
+ return PyCurses_ConvertToCchar_t(win, obj, pch);
+#endif
}
+#ifdef HAVE_NCURSESW
+
/* Pack a wide-character cell, routing the color pair through the
extended-color opts slot so it is not limited to a short (unlike the
chtype COLOR_PAIR field). Without that slot the pair must fit in the
}
#endif
+/* ---- Build-agnostic cell helpers over curses_cell_t -------------------- */
+
+/* Pack a cell's text (a str), attributes and color pair into *cell. On a
+ narrow build the text must be one character that encodes to a single byte.
+ Return 0, or -1 with an exception set. */
+static int
+curses_cell_pack(cursesmodule_state *state, curses_cell_t *cell,
+ PyObject *text, attr_t attr, int pair, const char *funcname)
+{
+#ifdef HAVE_NCURSESW
+ wchar_t wstr[CCHARW_MAX + 1];
+ if (PyCurses_ConvertToWideCell(text, wstr) < 0) {
+ return -1;
+ }
+ if (curses_setcchar(cell, wstr, attr, pair) == ERR) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(state->error, "setcchar() returned ERR");
+ }
+ return -1;
+ }
+ return 0;
+#else
+ /* A narrow cell holds one character; reject a longer string with the same
+ ValueError as the wide build, so the exception type is build-independent. */
+ if (PyUnicode_GET_LENGTH(text) != 1) {
+ PyErr_SetString(PyExc_ValueError,
+ "a character cell must be a single character");
+ return -1;
+ }
+ chtype ch;
+ if (!PyCurses_ConvertToChtype(NULL, text, &ch)) {
+ return -1;
+ }
+ /* Reject a pair that COLOR_PAIR()/PAIR_NUMBER() cannot round-trip (assumes
+ only that they are inverses). A wide build, or color_set(), can use
+ larger pairs. */
+ chtype color = COLOR_PAIR(pair);
+ if (pair < 0 || PAIR_NUMBER(color) != pair) {
+ PyErr_Format(PyExc_OverflowError,
+ "%s(): color pair %d does not fit in a chtype "
+ "(color_pair() can encode only pairs 0 to %d)",
+ funcname, pair, (int)PAIR_NUMBER(A_COLOR));
+ return -1;
+ }
+ *cell = (ch & A_CHARTEXT) | (attr & ~(attr_t)A_COLOR) | color;
+ return 0;
+#endif
+}
+
+/* Read a cell's attributes and color pair. Return 0, or -1 if wide getcchar()
+ fails (practically impossible). */
+static int
+curses_cell_attr_pair(cursesmodule_state *state, const curses_cell_t *cell,
+ attr_t *attr, int *pair)
+{
+#ifdef HAVE_NCURSESW
+ wchar_t wstr[CCHARW_MAX + 1];
+ if (curses_getcchar(cell, wstr, attr, pair) == ERR) {
+ PyErr_SetString(state->error, "getcchar() returned ERR");
+ return -1;
+ }
+ return 0;
+#else
+ *attr = *cell & A_ATTRIBUTES & ~(attr_t)A_COLOR;
+ *pair = PAIR_NUMBER(*cell & A_COLOR);
+ return 0;
+#endif
+}
+
+/* Build a str from a cell's text (decoding the stored byte on a narrow build).
+ Return a new reference, or NULL with an exception set. */
+static PyObject *
+curses_cell_text(cursesmodule_state *state, const curses_cell_t *cell)
+{
+#ifdef HAVE_NCURSESW
+ wchar_t wstr[CCHARW_MAX + 1];
+ attr_t attr;
+ int pair;
+ if (curses_getcchar(cell, wstr, &attr, &pair) == ERR) {
+ PyErr_SetString(state->error, "getcchar() returned ERR");
+ return NULL;
+ }
+ return PyUnicode_FromWideChar(wstr, -1);
+#else
+ char ch = (char)(*cell & A_CHARTEXT);
+ return PyUnicode_Decode(&ch, 1, curses_screen_encoding, NULL);
+#endif
+}
+
+/* Compare two cells by value (text, attributes, pair). Return 1, 0, or -1 if
+ wide getcchar() fails (practically impossible). */
+static int
+curses_cell_equal(cursesmodule_state *state, const curses_cell_t *a,
+ const curses_cell_t *b)
+{
+#ifdef HAVE_NCURSESW
+ wchar_t wa[CCHARW_MAX + 1], wb[CCHARW_MAX + 1];
+ attr_t aa, ab;
+ int pa, pb;
+ if (curses_getcchar(a, wa, &aa, &pa) == ERR ||
+ curses_getcchar(b, wb, &ab, &pb) == ERR)
+ {
+ PyErr_SetString(state->error, "getcchar() returned ERR");
+ return -1;
+ }
+ return (aa == ab && pa == pb && wcscmp(wa, wb) == 0);
+#else
+ return *a == *b;
+#endif
+}
+
+/* Hash a cell by value, consistent with curses_cell_equal. */
+static Py_hash_t
+curses_cell_hash(cursesmodule_state *state, const curses_cell_t *cell)
+{
+#ifdef HAVE_NCURSESW
+ return curses_cchar_hash(state, cell);
+#else
+ Py_hash_t h = Py_HashBuffer(cell, sizeof(*cell));
+ return h == -1 ? -2 : h;
+#endif
+}
+
static int
color_allow_default_converter(PyObject *arg, void *ptr)
{
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=57b994c97cbd5e80]*/
-#ifdef HAVE_NCURSESW
/* -------------------------------------------------------*/
-/* Complex character objects (styled wide-character cells) */
+/* Complex character objects (styled character cells) */
/* -------------------------------------------------------*/
-/* Wrap a cchar_t in a new complexchar object (the read side: in_wch,
- getbkgrnd, ...). The object simply owns a copy of the cell. */
+/* Wrap a cell in a new complexchar (the read side: in_wch, getbkgrnd). The
+ object owns a copy of the cell. */
static PyObject *
-PyCursesComplexChar_New(cursesmodule_state *state, const cchar_t *wcval)
+PyCursesComplexChar_New(cursesmodule_state *state, const curses_cell_t *wcval)
{
PyCursesComplexCharObject *cc =
PyObject_New(PyCursesComplexCharObject, state->complexchar_type);
return (PyObject *)cc;
}
-/* Decode the cell, raising curses.error on the (practically impossible)
- getcchar() failure. */
-static int
-complexchar_unpack(PyObject *self, wchar_t *wstr, attr_t *attrs, int *pair)
-{
- cchar_t *cval = &_PyCursesComplexCharObject_CAST(self)->cval;
- if (curses_getcchar(cval, wstr, attrs, pair) == ERR) {
- cursesmodule_state *state =
- get_cursesmodule_state_by_cls(Py_TYPE(self));
- PyErr_SetString(state->error, "getcchar() returned ERR");
- return -1;
- }
- return 0;
-}
-
/*[clinic input]
@classmethod
_curses.complexchar.__new__ as complexchar_new
PyErr_SetString(PyExc_ValueError, "color pair is less than 0");
return NULL;
}
- wchar_t wstr[CCHARW_MAX + 1];
- if (PyCurses_ConvertToWideCell(text, wstr) < 0) {
- return NULL;
- }
- cchar_t cval;
- if (curses_setcchar(&cval, wstr, attr, pair) == ERR) {
- if (!PyErr_Occurred()) {
- cursesmodule_state *state = get_cursesmodule_state_by_cls(type);
- PyErr_SetString(state->error, "setcchar() returned ERR");
- }
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(type);
+ curses_cell_t cval;
+ if (curses_cell_pack(state, &cval, text, attr, pair, "complexchar") < 0) {
return NULL;
}
PyCursesComplexCharObject *cc =
static PyObject *
complexchar_str(PyObject *self)
{
- wchar_t wstr[CCHARW_MAX + 1];
- attr_t attrs;
- int pair;
- if (complexchar_unpack(self, wstr, &attrs, &pair) < 0) {
- return NULL;
- }
- return PyUnicode_FromWideChar(wstr, -1);
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
+ return curses_cell_text(state, &_PyCursesComplexCharObject_CAST(self)->cval);
}
static PyObject *
complexchar_repr(PyObject *self)
{
- wchar_t wstr[CCHARW_MAX + 1];
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
+ const curses_cell_t *cval = &_PyCursesComplexCharObject_CAST(self)->cval;
attr_t attrs;
int pair;
- if (complexchar_unpack(self, wstr, &attrs, &pair) < 0) {
+ if (curses_cell_attr_pair(state, cval, &attrs, &pair) < 0) {
return NULL;
}
- PyObject *text = PyUnicode_FromWideChar(wstr, -1);
+ PyObject *text = curses_cell_text(state, cval);
if (text == NULL) {
return NULL;
}
static PyObject *
complexchar_get_attr(PyObject *self, void *Py_UNUSED(closure))
{
- wchar_t wstr[CCHARW_MAX + 1];
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
attr_t attrs;
int pair;
- if (complexchar_unpack(self, wstr, &attrs, &pair) < 0) {
+ if (curses_cell_attr_pair(state,
+ &_PyCursesComplexCharObject_CAST(self)->cval, &attrs, &pair) < 0) {
return NULL;
}
return PyLong_FromUnsignedLong((unsigned long)attrs);
static PyObject *
complexchar_get_pair(PyObject *self, void *Py_UNUSED(closure))
{
- wchar_t wstr[CCHARW_MAX + 1];
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
attr_t attrs;
int pair;
- if (complexchar_unpack(self, wstr, &attrs, &pair) < 0) {
+ if (curses_cell_attr_pair(state,
+ &_PyCursesComplexCharObject_CAST(self)->cval, &attrs, &pair) < 0) {
return NULL;
}
return PyLong_FromLong(pair);
{
Py_RETURN_NOTIMPLEMENTED;
}
- wchar_t wstr1[CCHARW_MAX + 1], wstr2[CCHARW_MAX + 1];
- attr_t attrs1, attrs2;
- int pair1, pair2;
- if (complexchar_unpack(self, wstr1, &attrs1, &pair1) < 0 ||
- complexchar_unpack(other, wstr2, &attrs2, &pair2) < 0)
- {
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
+ int equal = curses_cell_equal(state,
+ &_PyCursesComplexCharObject_CAST(self)->cval,
+ &_PyCursesComplexCharObject_CAST(other)->cval);
+ if (equal < 0) {
return NULL;
}
- int equal = (attrs1 == attrs2 && pair1 == pair2 &&
- wcscmp(wstr1, wstr2) == 0);
return PyBool_FromLong(equal == (op == Py_EQ));
}
complexchar_hash(PyObject *self)
{
cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
- return curses_cchar_hash(state, &_PyCursesComplexCharObject_CAST(self)->cval);
+ return curses_cell_hash(state, &_PyCursesComplexCharObject_CAST(self)->cval);
}
static PyGetSetDef complexchar_getsets[] = {
and color pair 0) -- into *out. Return 0 on success, -1 with an exception
set otherwise. */
static int
-curses_pack_cell(cursesmodule_state *state, PyObject *item, cchar_t *out)
+curses_pack_cell(cursesmodule_state *state, PyObject *item, curses_cell_t *out)
{
if (Py_IS_TYPE(item, state->complexchar_type)) {
*out = _PyCursesComplexCharObject_CAST(item)->cval;
return 0;
}
if (PyUnicode_Check(item)) {
- wchar_t wstr[CCHARW_MAX + 1];
- if (PyCurses_ConvertToWideCell(item, wstr) < 0) {
- return -1;
- }
- if (curses_setcchar(out, wstr, A_NORMAL, 0) == ERR) {
- PyErr_SetString(state->error, "setcchar() returned ERR");
- return -1;
- }
- return 0;
+ return curses_cell_pack(state, out, item, A_NORMAL, 0, "complexstr");
}
PyErr_Format(PyExc_TypeError,
"complexstr cell must be a complexchar or a str, not %T",
/* Wrap a buffer of len cells in a new complexstr, copying them in. tp_alloc
sizes the variable-size object for len cells and sets ob_size. */
static PyObject *
-PyCursesComplexStr_New(cursesmodule_state *state, const cchar_t *cells,
+PyCursesComplexStr_New(cursesmodule_state *state, const curses_cell_t *cells,
Py_ssize_t len)
{
PyTypeObject *type = state->complexstr_type;
PyObject *res = type->tp_alloc(type, len);
if (res != NULL && len > 0) {
memcpy(_PyCursesComplexStrObject_CAST(res)->cells, cells,
- (size_t)len * sizeof(cchar_t));
+ (size_t)len * sizeof(curses_cell_t));
}
return res;
}
complexstr_from_string(cursesmodule_state *state, PyObject *str,
attr_t attr, int pair)
{
+#ifdef HAVE_NCURSESW
Py_ssize_t n;
wchar_t *wbuf = PyUnicode_AsWideCharString(str, &n);
if (wbuf == NULL) {
return NULL;
}
- cchar_t *cells = n > 0 ? PyMem_New(cchar_t, n) : NULL;
+ curses_cell_t *cells = n > 0 ? PyMem_New(curses_cell_t, n) : NULL;
if (n > 0 && cells == NULL) {
PyMem_Free(wbuf);
return PyErr_NoMemory();
PyMem_Free(cells);
PyMem_Free(wbuf);
return res;
+#else
+ /* Validate the pair once (it is the same for every cell); see
+ curses_cell_pack() for the round-trip rationale. */
+ chtype color = COLOR_PAIR(pair);
+ if (pair < 0 || PAIR_NUMBER(color) != pair) {
+ PyErr_Format(PyExc_OverflowError,
+ "complexstr(): color pair %d does not fit in a chtype "
+ "(color_pair() can encode only pairs 0 to %d)",
+ pair, (int)PAIR_NUMBER(A_COLOR));
+ return NULL;
+ }
+ /* Encode the whole string at once; equal byte and character counts confirm
+ every character is a single byte (one per cell). */
+ PyObject *bytes = PyUnicode_AsEncodedString(str, curses_screen_encoding,
+ NULL);
+ if (bytes == NULL) {
+ return NULL;
+ }
+ Py_ssize_t n = PyBytes_GET_SIZE(bytes);
+ if (n != PyUnicode_GET_LENGTH(str)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "a character cell does not fit in a single byte");
+ Py_DECREF(bytes);
+ return NULL;
+ }
+ curses_cell_t *cells = n > 0 ? PyMem_New(curses_cell_t, n) : NULL;
+ if (n > 0 && cells == NULL) {
+ Py_DECREF(bytes);
+ return PyErr_NoMemory();
+ }
+ const unsigned char *buf = (const unsigned char *)PyBytes_AS_STRING(bytes);
+ attr_t cattr = attr & ~(attr_t)A_COLOR;
+ for (Py_ssize_t i = 0; i < n; i++) {
+ cells[i] = ((chtype)buf[i] & A_CHARTEXT) | cattr | color;
+ }
+ PyObject *res = PyCursesComplexStr_New(state, cells, n);
+ PyMem_Free(cells);
+ Py_DECREF(bytes);
+ return res;
+#endif
}
/*[clinic input]
Py_DECREF(seq);
return NULL;
}
- cchar_t *out = _PyCursesComplexStrObject_CAST(res)->cells;
+ curses_cell_t *out = _PyCursesComplexStrObject_CAST(res)->cells;
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *item = PySequence_Fast_GET_ITEM(seq, i); // borrowed
if (curses_pack_cell(state, item, &out[i]) < 0) {
complexstr_getcell(PyObject *self, Py_ssize_t i)
{
cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
- cchar_t *cells = _PyCursesComplexStrObject_CAST(self)->cells;
+ curses_cell_t *cells = _PyCursesComplexStrObject_CAST(self)->cells;
return PyCursesComplexChar_New(state, &cells[i]);
}
if (res == NULL) {
return NULL;
}
- cchar_t *out = _PyCursesComplexStrObject_CAST(res)->cells;
+ curses_cell_t *out = _PyCursesComplexStrObject_CAST(res)->cells;
for (Py_ssize_t i = 0, idx = start; i < slicelen; i++, idx += step) {
out[i] = s->cells[idx];
}
if (res == NULL) {
return NULL;
}
- cchar_t *out = _PyCursesComplexStrObject_CAST(res)->cells;
+ curses_cell_t *out = _PyCursesComplexStrObject_CAST(res)->cells;
if (Py_SIZE(sa)) {
- memcpy(out, sa->cells, (size_t)Py_SIZE(sa) * sizeof(cchar_t));
+ memcpy(out, sa->cells, (size_t)Py_SIZE(sa) * sizeof(curses_cell_t));
}
if (Py_SIZE(sb)) {
- memcpy(out + Py_SIZE(sa), sb->cells, (size_t)Py_SIZE(sb) * sizeof(cchar_t));
+ memcpy(out + Py_SIZE(sa), sb->cells, (size_t)Py_SIZE(sb) * sizeof(curses_cell_t));
}
return res;
}
complexstr_str(PyObject *self)
{
PyCursesComplexStrObject *s = _PyCursesComplexStrObject_CAST(self);
- if (Py_SIZE(s) == 0) {
+ Py_ssize_t len = Py_SIZE(s);
+ if (len == 0) {
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
}
- wchar_t *buf = PyMem_New(wchar_t, Py_SIZE(s) * CCHARW_MAX + 1);
+#ifdef HAVE_NCURSESW
+ wchar_t *buf = PyMem_New(wchar_t, len * CCHARW_MAX + 1);
if (buf == NULL) {
return PyErr_NoMemory();
}
Py_ssize_t pos = 0;
- for (Py_ssize_t i = 0; i < Py_SIZE(s); i++) {
+ for (Py_ssize_t i = 0; i < len; i++) {
attr_t attrs;
int pair;
/* getcchar() writes the cell's text (and a terminator) at buf + pos;
PyObject *res = PyUnicode_FromWideChar(buf, pos);
PyMem_Free(buf);
return res;
+#else
+ /* Each cell stores a single locale-encoded byte; decode them all at once. */
+ char *buf = PyMem_Malloc(len);
+ if (buf == NULL) {
+ return PyErr_NoMemory();
+ }
+ for (Py_ssize_t i = 0; i < len; i++) {
+ buf[i] = (char)(s->cells[i] & A_CHARTEXT);
+ }
+ PyObject *res = PyUnicode_Decode(buf, len, curses_screen_encoding, NULL);
+ PyMem_Free(buf);
+ return res;
+#endif
}
static PyObject *
/* Combine the per-cell hashes like a tuple. */
Py_uhash_t acc = _PyTuple_HASH_XXPRIME_5;
for (Py_ssize_t i = 0; i < Py_SIZE(s); i++) {
- Py_hash_t lane = curses_cchar_hash(state, &s->cells[i]);
+ Py_hash_t lane = curses_cell_hash(state, &s->cells[i]);
if (lane == -1) {
return -1;
}
}
PyCursesComplexStrObject *a = _PyCursesComplexStrObject_CAST(self);
PyCursesComplexStrObject *b = _PyCursesComplexStrObject_CAST(other);
+ cursesmodule_state *state = get_cursesmodule_state_by_cls(Py_TYPE(self));
int equal = (Py_SIZE(a) == Py_SIZE(b));
for (Py_ssize_t i = 0; equal && i < Py_SIZE(a); i++) {
- wchar_t wa[CCHARW_MAX + 1], wb[CCHARW_MAX + 1];
- attr_t aa, ab;
- int pa, pb;
- if (curses_getcchar(&a->cells[i], wa, &aa, &pa) == ERR ||
- curses_getcchar(&b->cells[i], wb, &ab, &pb) == ERR)
- {
- cursesmodule_state *state =
- get_cursesmodule_state_by_cls(Py_TYPE(self));
- PyErr_SetString(state->error, "getcchar() returned ERR");
+ int eq = curses_cell_equal(state, &a->cells[i], &b->cells[i]);
+ if (eq < 0) {
return NULL;
}
- equal = (aa == ab && pa == pb && wcscmp(wa, wb) == 0);
+ equal = eq;
}
return PyBool_FromLong(equal == (op == Py_EQ));
}
int use_xy, int y, int x, int n_limit, int insert,
const char *funcname)
{
- const cchar_t *cells = _PyCursesComplexStrObject_CAST(obj)->cells;
+ const curses_cell_t *cells = _PyCursesComplexStrObject_CAST(obj)->cells;
Py_ssize_t count = Py_SIZE(obj);
if (n_limit >= 0 && count > n_limit) {
int rtn;
const char *cfuncname;
if (insert) {
- /* There is no batch cchar_t insert; insert the cells right-to-left at
- the position so they end up in order. */
+ /* No batch cell insert exists; insert the cells right-to-left so they
+ end up in order. */
if (use_xy && wmove(self->win, y, x) == ERR) {
curses_window_set_error(self, "wmove", funcname);
return NULL;
}
rtn = OK;
+#ifdef HAVE_NCURSESW
cfuncname = "wins_wch";
for (Py_ssize_t i = count - 1; i >= 0; i--) {
rtn = wins_wch(self->win, &cells[i]);
break;
}
}
+#else
+ cfuncname = "winsch";
+ for (Py_ssize_t i = count - 1; i >= 0; i--) {
+ rtn = winsch(self->win, cells[i]);
+ if (rtn == ERR) {
+ break;
+ }
+ }
+#endif
}
else if (use_xy) {
+#ifdef HAVE_NCURSESW
rtn = mvwadd_wchnstr(self->win, y, x, cells, (int)count);
cfuncname = "mvwadd_wchnstr";
+#else
+ rtn = mvwaddchnstr(self->win, y, x, cells, (int)count);
+ cfuncname = "mvwaddchnstr";
+#endif
}
else {
+#ifdef HAVE_NCURSESW
rtn = wadd_wchnstr(self->win, cells, (int)count);
cfuncname = "wadd_wchnstr";
+#else
+ rtn = waddchnstr(self->win, cells, (int)count);
+ cfuncname = "waddchnstr";
+#endif
}
return curses_window_check_err(self, rtn, cfuncname, funcname);
}
-#endif
-
/*****************************************************************************
The Window Object
******************************************************************************/
}
else
#else
- type = PyCurses_ConvertToCchar_t(self, ch, &cch);
+ type = PyCurses_ConvertToCell(self, ch, attr, group_right_1, "addch", &cch);
#endif
if (type == 1) {
if (coordinates_group) {
int use_xy = group_left_1, use_attr = group_right_1;
const char *funcname;
-#ifdef HAVE_NCURSESW
{
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
if (Py_IS_TYPE(str, state->complexstr_type)) {
-1, 0, "addstr");
}
}
+#ifdef HAVE_NCURSESW
strtype = PyCurses_ConvertToString(self, str, &bytesobj, &wstr);
#else
strtype = PyCurses_ConvertToString(self, str, &bytesobj, NULL);
int use_xy = group_left_1, use_attr = group_right_1;
const char *funcname;
-#ifdef HAVE_NCURSESW
{
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
if (Py_IS_TYPE(str, state->complexstr_type)) {
n, 0, "addnstr");
}
}
+#ifdef HAVE_NCURSESW
strtype = PyCurses_ConvertToString(self, str, &bytesobj, &wstr);
#else
strtype = PyCurses_ConvertToString(self, str, &bytesobj, NULL);
return curses_window_check_err(self, rtn, "wbkgrnd", "bkgd");
}
#else
- if (!PyCurses_ConvertToChtype(self, ch, &bkgd))
+ if (!PyCurses_ConvertToCell(self, ch, attr, group_right_1, "bkgd", &bkgd))
return NULL;
#endif
Py_RETURN_NONE;
}
#else
- if (!PyCurses_ConvertToChtype(self, ch, &bkgd))
+ if (!PyCurses_ConvertToCell(self, ch, attr, group_right_1, "bkgdset", &bkgd))
return NULL;
#endif
}
#else
for (i = 0; i < 8; i++) {
- if (objs[i] != NULL && !PyCurses_ConvertToChtype(self, objs[i], &ch[i]))
+ if (objs[i] != NULL &&
+ !PyCurses_ConvertToCell(self, objs[i], A_NORMAL, 0, "border", &ch[i]))
return NULL;
}
#endif
}
#else
if (group_right_1) {
- if (!PyCurses_ConvertToChtype(self, verch, &ch1)) {
+ if (!PyCurses_ConvertToCell(self, verch, A_NORMAL, 0, "box", &ch1)) {
return NULL;
}
- if (!PyCurses_ConvertToChtype(self, horch, &ch2)) {
+ if (!PyCurses_ConvertToCell(self, horch, A_NORMAL, 0, "box", &ch2)) {
return NULL;
}
}
return curses_window_check_err(self, rtn, funcname, "echochar");
}
#else
- if (!PyCurses_ConvertToChtype(self, ch, &ch_))
+ if (!PyCurses_ConvertToCell(self, ch, attr, group_right_1, "echochar", &ch_))
return NULL;
#endif
return PyLong_FromLong(rtn);
}
-#ifdef HAVE_NCURSESW
/*[clinic input]
_curses.window.in_wch
int y, int x)
/*[clinic end generated code: output=846ca8a82f2ecab4 input=a55dd215367dfbb1]*/
{
- cchar_t wcval;
+ curses_cell_t wcval;
+ cursesmodule_state *state = get_cursesmodule_state_by_win(self);
+#ifdef HAVE_NCURSESW
int rtn;
const char *funcname;
if (group_right_1) {
curses_window_set_error(self, funcname, "in_wch");
return NULL;
}
- cursesmodule_state *state = get_cursesmodule_state_by_win(self);
+#else
+ chtype c;
+ const char *funcname;
+ if (group_right_1) {
+ c = mvwinch(self->win, y, x);
+ funcname = "mvwinch";
+ }
+ else {
+ c = winch(self->win);
+ funcname = "winch";
+ }
+ if (c == (chtype)ERR) {
+ curses_window_set_error(self, funcname, "in_wch");
+ return NULL;
+ }
+ wcval = c;
+#endif
return PyCursesComplexChar_New(state, &wcval);
}
_curses_window_getbkgrnd_impl(PyCursesWindowObject *self)
/*[clinic end generated code: output=afec19cad00eff71 input=e06bf3d6bf90d2ec]*/
{
- cchar_t wcval;
+ curses_cell_t wcval;
+ cursesmodule_state *state = get_cursesmodule_state_by_win(self);
+#ifdef HAVE_NCURSESW
if (wgetbkgrnd(self->win, &wcval) == ERR) {
curses_window_set_error(self, "wgetbkgrnd", "getbkgrnd");
return NULL;
}
- cursesmodule_state *state = get_cursesmodule_state_by_win(self);
+#else
+ chtype c = getbkgd(self->win);
+ if (c == (chtype)ERR) {
+ curses_window_set_error(self, "getbkgd", "getbkgrnd");
+ return NULL;
+ }
+ wcval = c;
+#endif
return PyCursesComplexChar_New(state, &wcval);
}
-#endif /* HAVE_NCURSESW */
-
static PyObject *
curses_check_signals_on_input_error(PyCursesWindowObject *self,
const char *curses_funcname,
return NULL;
}
#else
- if (!PyCurses_ConvertToChtype(self, ch, &ch_))
+ if (!PyCurses_ConvertToCell(self, ch, attr, group_right_1, "hline", &ch_))
return NULL;
#endif
if (group_left_1) {
return curses_window_check_err(self, rtn, funcname, "insch");
}
#else
- if (!PyCurses_ConvertToChtype(self, ch, &ch_))
+ if (!PyCurses_ConvertToCell(self, ch, attr, group_right_1, "insch", &ch_))
return NULL;
#endif
PyMem_Free(buf);
return res;
}
+#endif /* HAVE_NCURSESW */
PyDoc_STRVAR(_curses_window_in_wstr__doc__,
"in_wstr([y, x,] n=2047)\n"
}
n = Py_MIN(n, max_buf_size - 1);
+#ifdef HAVE_NCURSESW
wchar_t *buf = PyMem_New(wchar_t, n + 1);
if (buf == NULL) {
return PyErr_NoMemory();
PyObject *res = PyUnicode_FromWideChar(buf, -1);
PyMem_Free(buf);
return res;
+#else
+ /* Without the wide library, read the locale-encoded bytes and decode them
+ with the window's encoding. */
+ char *buf = PyMem_New(char, n + 1);
+ if (buf == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ if (use_xy) {
+ rtn = mvwinnstr(self->win, y, x, buf, n);
+ }
+ else {
+ rtn = winnstr(self->win, buf, n);
+ }
+
+ if (rtn == ERR) {
+ PyMem_Free(buf);
+ return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
+ }
+ PyObject *res = PyUnicode_Decode(buf, strlen(buf), self->encoding, NULL);
+ PyMem_Free(buf);
+ return res;
+#endif
}
PyDoc_STRVAR(_curses_window_in_wchstr__doc__,
}
n = Py_MIN(n, max_buf_size - 1);
- cchar_t *buf = PyMem_New(cchar_t, n + 1);
+ cursesmodule_state *state = get_cursesmodule_state_by_win(self);
+ curses_cell_t *buf = PyMem_New(curses_cell_t, n + 1);
if (buf == NULL) {
return PyErr_NoMemory();
}
+#ifdef HAVE_NCURSESW
if (use_xy) {
rtn = mvwin_wchnstr(self->win, y, x, buf, n);
}
rtn = win_wchnstr(self->win, buf, n);
}
- cursesmodule_state *state = get_cursesmodule_state_by_win(self);
if (rtn == ERR) {
PyMem_Free(buf);
return PyCursesComplexStr_New(state, NULL, 0);
}
count++;
}
+#else
+ /* winchnstr() is not guaranteed (SVr4) to terminate the array, so pre-zero
+ it and stop at the first empty cell; a painted cell always holds at least
+ a space, never 0. */
+ memset(buf, 0, ((size_t)n + 1) * sizeof(curses_cell_t));
+ if (use_xy) {
+ rtn = mvwinchnstr(self->win, y, x, buf, n);
+ }
+ else {
+ rtn = winchnstr(self->win, buf, n);
+ }
+
+ if (rtn == ERR) {
+ PyMem_Free(buf);
+ return PyCursesComplexStr_New(state, NULL, 0);
+ }
+
+ Py_ssize_t count = 0;
+ while (count < (Py_ssize_t)n && buf[count] != 0) {
+ count++;
+ }
+#endif
PyObject *res = PyCursesComplexStr_New(state, buf, count);
PyMem_Free(buf);
return res;
}
-#endif /* HAVE_NCURSESW */
/*[clinic input]
_curses.window.insstr
int use_xy = group_left_1, use_attr = group_right_1;
const char *funcname;
-#ifdef HAVE_NCURSESW
{
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
if (Py_IS_TYPE(str, state->complexstr_type)) {
-1, 1, "insstr");
}
}
+#ifdef HAVE_NCURSESW
strtype = PyCurses_ConvertToString(self, str, &bytesobj, &wstr);
#else
strtype = PyCurses_ConvertToString(self, str, &bytesobj, NULL);
int use_xy = group_left_1, use_attr = group_right_1;
const char *funcname;
-#ifdef HAVE_NCURSESW
{
cursesmodule_state *state = get_cursesmodule_state_by_win(self);
if (Py_IS_TYPE(str, state->complexstr_type)) {
n, 1, "insnstr");
}
}
+#ifdef HAVE_NCURSESW
strtype = PyCurses_ConvertToString(self, str, &bytesobj, &wstr);
#else
strtype = PyCurses_ConvertToString(self, str, &bytesobj, NULL);
return NULL;
}
#else
- if (!PyCurses_ConvertToChtype(self, ch, &ch_))
+ if (!PyCurses_ConvertToCell(self, ch, attr, group_right_1, "vline", &ch_))
return NULL;
#endif
if (group_left_1) {
#define clinic_state() (get_cursesmodule_state_by_cls(Py_TYPE(self)))
#include "clinic/_cursesmodule.c.h"
-#ifdef HAVE_NCURSESW
static PyType_Slot PyCursesComplexChar_Type_slots[] = {
{Py_tp_doc, (void *)complexchar_new__doc__},
{Py_tp_new, complexchar_new},
static PyType_Spec PyCursesComplexStr_Type_spec = {
.name = "curses.complexstr",
.basicsize = offsetof(PyCursesComplexStrObject, cells),
- .itemsize = sizeof(cchar_t),
+ .itemsize = sizeof(curses_cell_t),
.flags = Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_IMMUTABLETYPE
| Py_TPFLAGS_HEAPTYPE,
.slots = PyCursesComplexStr_Type_slots
};
-#endif
#undef clinic_state
#if defined(HAVE_CURSES_USE_SCREEN) || defined(HAVE_CURSES_USE_WINDOW)
"instr", PyCursesWindow_instr, METH_VARARGS,
_curses_window_instr__doc__
},
-#ifdef HAVE_NCURSESW
{
"in_wstr", PyCursesWindow_in_wstr, METH_VARARGS,
_curses_window_in_wstr__doc__
"in_wchstr", PyCursesWindow_in_wchstr, METH_VARARGS,
_curses_window_in_wchstr__doc__
},
-#endif
_CURSES_WINDOW_IS_LINETOUCHED_METHODDEF
{"is_wintouched", PyCursesWindow_is_wintouched, METH_NOARGS,
"is_wintouched($self, /)\n--\n\n"
Py_VISIT(state->error);
Py_VISIT(state->window_type);
Py_VISIT(state->screen_type);
-#ifdef HAVE_NCURSESW
Py_VISIT(state->complexchar_type);
Py_VISIT(state->complexstr_type);
-#endif
Py_VISIT(state->topscreen);
return 0;
}
Py_CLEAR(state->error);
Py_CLEAR(state->window_type);
Py_CLEAR(state->screen_type);
-#ifdef HAVE_NCURSESW
Py_CLEAR(state->complexchar_type);
Py_CLEAR(state->complexstr_type);
-#endif
Py_CLEAR(state->topscreen);
return 0;
}
if (PyModule_AddType(module, state->screen_type) < 0) {
return -1;
}
-#ifdef HAVE_NCURSESW
state->complexchar_type = (PyTypeObject *)PyType_FromModuleAndSpec(
module, &PyCursesComplexChar_Type_spec, NULL);
if (state->complexchar_type == NULL) {
if (PyModule_AddType(module, state->complexstr_type) < 0) {
return -1;
}
-#endif
/* Add some symbolic constants to the module */
PyObject *module_dict = PyModule_GetDict(module);