From 09c1b6a12c572cb3db32352bc604f7cdd84ede24 Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Wed, 11 Oct 2023 18:53:59 +0200 Subject: [PATCH 1/6] use is_pad macro, not function, if available --- Include/py_curses.h | 5 +-- Modules/_cursesmodule.c | 46 +++++++++----------------- Modules/clinic/_cursesmodule.c.h | 36 +-------------------- configure | 55 +++++++++++++++++++++++++------- configure.ac | 5 ++- 5 files changed, 67 insertions(+), 80 deletions(-) diff --git a/Include/py_curses.h b/Include/py_curses.h index e46b08e9cc414e..46f060d192091d 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -23,7 +23,7 @@ # endif #endif -#if !defined(HAVE_CURSES_IS_PAD) && defined(WINDOW_HAS_FLAGS) +#if defined(WINDOW_HAS_FLAGS) /* The following definition is necessary for ncurses 5.7; without it, some of [n]curses.h set NCURSES_OPAQUE to 1, and then Python can't get at the WINDOW flags field. */ @@ -39,7 +39,8 @@ #ifdef HAVE_NCURSES_H /* configure was checking , but we will use , which has some or all these features. */ -#if !defined(WINDOW_HAS_FLAGS) && !(NCURSES_OPAQUE+0) +#if !defined(WINDOW_HAS_FLAGS) && \ + (NCURSES_VERSION_PATCH+0 < 20070303 || !(NCURSES_OPAQUE+0)) #define WINDOW_HAS_FLAGS 1 #endif #if !defined(HAVE_CURSES_IS_PAD) && NCURSES_VERSION_PATCH+0 >= 20090906 diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index d339a8aa798361..2436949deefb2c 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1155,11 +1155,19 @@ int py_mvwdelch(WINDOW *w, int y, int x) } #endif +static inline bool +py_is_pad(const WINDOW *win) { #if defined(HAVE_CURSES_IS_PAD) -#define py_is_pad(win) is_pad(win) -#elif defined(WINDOW_HAS_FLAGS) -#define py_is_pad(win) ((win) ? ((win)->_flags & _ISPAD) != 0 : FALSE) + // is_pad is defined, either as a macro or as a function + return is_pad(win); +#elif WINDOW_HAS_FLAGS + // is_pad is not defined, but we can inspect struct internals + return (win ? (win->_flags & _ISPAD) != 0 : FALSE); +#else + // probably not ncurses + return FALSE; #endif +} /* chgat, added by Fabian Kreutz */ #ifdef HAVE_CURSES_WCHGAT @@ -1332,15 +1340,13 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch, if (!PyCurses_ConvertToChtype(self, ch, &ch_)) return NULL; -#ifdef py_is_pad if (py_is_pad(self->win)) { return PyCursesCheckERR(pechochar(self->win, ch_ | (attr_t)attr), "echochar"); - } - else -#endif + } else { return PyCursesCheckERR(wechochar(self->win, ch_ | (attr_t)attr), "echochar"); + } } #ifdef NCURSES_MOUSE_VERSION @@ -1975,7 +1981,6 @@ _curses_window_is_linetouched_impl(PyCursesWindowObject *self, int line) return PyBool_FromLong(erg); } -#ifdef py_is_pad /*[clinic input] _curses.window.noutrefresh @@ -2002,25 +2007,9 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self, int sminrow, int smincol, int smaxrow, int smaxcol) /*[clinic end generated code: output=809a1f3c6a03e23e input=3e56898388cd739e]*/ -#else -/*[clinic input] -_curses.window.noutrefresh - -Mark for refresh but wait. - -This function updates the data structure representing the desired state of the -window, but does not force an update of the physical screen. To accomplish -that, call doupdate(). -[clinic start generated code]*/ - -static PyObject * -_curses_window_noutrefresh_impl(PyCursesWindowObject *self) -/*[clinic end generated code: output=6ef6dec666643fee input=876902e3fa431dbd]*/ -#endif { int rtn; -#ifdef py_is_pad if (py_is_pad(self->win)) { if (!group_right_1) { PyErr_SetString(PyCursesError, @@ -2039,7 +2028,6 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self) "noutrefresh() takes no arguments (6 given)"); return NULL; } -#endif Py_BEGIN_ALLOW_THREADS rtn = wnoutrefresh(self->win); Py_END_ALLOW_THREADS @@ -2244,7 +2232,6 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, { int rtn; -#ifdef py_is_pad if (py_is_pad(self->win)) { if (!group_right_1) { PyErr_SetString(PyCursesError, @@ -2257,7 +2244,6 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, Py_END_ALLOW_THREADS return PyCursesCheckERR(rtn, "prefresh"); } -#endif if (group_right_1) { PyErr_SetString(PyExc_TypeError, "refresh() takes no arguments (6 given)"); @@ -2320,13 +2306,11 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1, WINDOW *win; /* printf("Subwin: %i %i %i %i \n", nlines, ncols, begin_y, begin_x); */ -#ifdef py_is_pad if (py_is_pad(self->win)) { win = subpad(self->win, nlines, ncols, begin_y, begin_x); - } - else -#endif + } else { win = subwin(self->win, nlines, ncols, begin_y, begin_x); + } if (win == NULL) { PyErr_SetString(PyCursesError, catchall_NULL); diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index ecc3c059d03c90..f74dd531e44e62 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -1273,8 +1273,6 @@ _curses_window_is_linetouched(PyCursesWindowObject *self, PyObject *arg) return return_value; } -#if defined(py_is_pad) - PyDoc_STRVAR(_curses_window_noutrefresh__doc__, "noutrefresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])\n" "Mark for refresh but wait.\n" @@ -1323,34 +1321,6 @@ _curses_window_noutrefresh(PyCursesWindowObject *self, PyObject *args) return return_value; } -#endif /* defined(py_is_pad) */ - -#if !defined(py_is_pad) - -PyDoc_STRVAR(_curses_window_noutrefresh__doc__, -"noutrefresh($self, /)\n" -"--\n" -"\n" -"Mark for refresh but wait.\n" -"\n" -"This function updates the data structure representing the desired state of the\n" -"window, but does not force an update of the physical screen. To accomplish\n" -"that, call doupdate()."); - -#define _CURSES_WINDOW_NOUTREFRESH_METHODDEF \ - {"noutrefresh", (PyCFunction)_curses_window_noutrefresh, METH_NOARGS, _curses_window_noutrefresh__doc__}, - -static PyObject * -_curses_window_noutrefresh_impl(PyCursesWindowObject *self); - -static PyObject * -_curses_window_noutrefresh(PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _curses_window_noutrefresh_impl(self); -} - -#endif /* !defined(py_is_pad) */ - PyDoc_STRVAR(_curses_window_overlay__doc__, "overlay(destwin, [sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol])\n" "Overlay the window on top of destwin.\n" @@ -4229,10 +4199,6 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #define _CURSES_WINDOW_GET_WCH_METHODDEF #endif /* !defined(_CURSES_WINDOW_GET_WCH_METHODDEF) */ -#ifndef _CURSES_WINDOW_NOUTREFRESH_METHODDEF - #define _CURSES_WINDOW_NOUTREFRESH_METHODDEF -#endif /* !defined(_CURSES_WINDOW_NOUTREFRESH_METHODDEF) */ - #ifndef _CURSES_FILTER_METHODDEF #define _CURSES_FILTER_METHODDEF #endif /* !defined(_CURSES_FILTER_METHODDEF) */ @@ -4312,4 +4278,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF #define _CURSES_USE_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=839faafb638935ea input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b138f4e120d1e086 input=a9049054013a1b77]*/ diff --git a/configure b/configure index c87f518382f2ec..ba82d7f318733f 100755 --- a/configure +++ b/configure @@ -25960,7 +25960,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26003,7 +26006,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26046,7 +26052,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26089,7 +26098,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26132,7 +26144,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26175,7 +26190,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26218,7 +26236,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26261,7 +26282,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26304,7 +26328,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26347,7 +26374,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { @@ -26390,7 +26420,10 @@ then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #define NCURSES_OPAQUE 0 + #include + int main (void) { diff --git a/configure.ac b/configure.ac index cd69f0ede54496..ab3a1104e3f83b 100644 --- a/configure.ac +++ b/configure.ac @@ -6347,7 +6347,10 @@ AC_DEFUN([PY_CHECK_CURSES_FUNC], [py_var], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( - [@%:@include ], [ + [[ + #define NCURSES_OPAQUE 0 + #include + ]], [ #ifndef $1 void *x=$1 #endif From bb2c852c59157b533bd57d192d068d2b0684a5cf Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Wed, 11 Oct 2023 18:54:58 +0200 Subject: [PATCH 2/6] get ncurses_version at runtime from curses_version() --- Modules/_cursesmodule.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 2436949deefb2c..f6d3dec497633f 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -4570,7 +4570,14 @@ make_ncurses_version(PyTypeObject *type) if (ncurses_version == NULL) { return NULL; } - + const char *str = curses_version(); + unsigned long major = 0, minor = 0, patch = 0; + if (!str || sscanf(str, "%*[^0-9]%lu.%lu.%lu", &major, &minor, &patch) < 3) { + // Fallback to header version, which cannot be that wrong + major = NCURSES_VERSION_MAJOR; + minor = NCURSES_VERSION_MINOR; + patch = NCURSES_VERSION_PATCH; + } #define SetIntItem(flag) \ PyStructSequence_SET_ITEM(ncurses_version, pos++, PyLong_FromLong(flag)); \ if (PyErr_Occurred()) { \ @@ -4578,9 +4585,9 @@ make_ncurses_version(PyTypeObject *type) return NULL; \ } - SetIntItem(NCURSES_VERSION_MAJOR) - SetIntItem(NCURSES_VERSION_MINOR) - SetIntItem(NCURSES_VERSION_PATCH) + SetIntItem(major) + SetIntItem(minor) + SetIntItem(patch) #undef SetIntItem return ncurses_version; From 73290a4979abc6337186aa55727a30ffce9019b6 Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Tue, 24 Oct 2023 12:39:14 +0200 Subject: [PATCH 3/6] add news entry --- .../next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst diff --git a/Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst b/Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst new file mode 100644 index 00000000000000..4fda69d332d50a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-10-24-12-39-04.gh-issue-109617.YoI8TV.rst @@ -0,0 +1,2 @@ +:mod:`ncurses`: fixed a crash that could occur on macOS 13 or earlier when +Python was built with Apple Xcode 15's SDK. From d92555203604d5ce062cf89e4cb1d01bd01c2459 Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Tue, 13 Feb 2024 11:59:35 +0100 Subject: [PATCH 4/6] revert py_is_pad to a macro --- Modules/_cursesmodule.c | 48 ++++++++++++++++++++++---------- Modules/clinic/_cursesmodule.c.h | 36 +++++++++++++++++++++++- configure.ac | 4 +-- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 727532ecb7904f..81c1acb7d59bfe 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1155,19 +1155,13 @@ int py_mvwdelch(WINDOW *w, int y, int x) } #endif -static inline bool -py_is_pad(const WINDOW *win) { #if defined(HAVE_CURSES_IS_PAD) - // is_pad is defined, either as a macro or as a function - return is_pad(win); -#elif WINDOW_HAS_FLAGS - // is_pad is not defined, but we can inspect struct internals - return (win ? (win->_flags & _ISPAD) != 0 : FALSE); -#else - // probably not ncurses - return FALSE; +// is_pad() is defined, either as a macro or as a function +#define py_is_pad(win) is_pad(win) +#elif defined(WINDOW_HAS_FLAGS) +// is_pad() is not defined, but we can inspect WINDOW structure members +#define py_is_pad(win) ((win) ? ((win)->_flags & _ISPAD) != 0 : FALSE) #endif -} /* chgat, added by Fabian Kreutz */ #ifdef HAVE_CURSES_WCHGAT @@ -1340,13 +1334,15 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch, if (!PyCurses_ConvertToChtype(self, ch, &ch_)) return NULL; +#ifdef py_is_pad if (py_is_pad(self->win)) { return PyCursesCheckERR(pechochar(self->win, ch_ | (attr_t)attr), "echochar"); - } else { + } + else +#endif return PyCursesCheckERR(wechochar(self->win, ch_ | (attr_t)attr), "echochar"); - } } #ifdef NCURSES_MOUSE_VERSION @@ -1981,6 +1977,7 @@ _curses_window_is_linetouched_impl(PyCursesWindowObject *self, int line) return PyBool_FromLong(erg); } +#ifdef py_is_pad /*[clinic input] _curses.window.noutrefresh @@ -2007,9 +2004,25 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self, int sminrow, int smincol, int smaxrow, int smaxcol) /*[clinic end generated code: output=809a1f3c6a03e23e input=3e56898388cd739e]*/ +#else +/*[clinic input] +_curses.window.noutrefresh + +Mark for refresh but wait. + +This function updates the data structure representing the desired state of the +window, but does not force an update of the physical screen. To accomplish +that, call doupdate(). +[clinic start generated code]*/ + +static PyObject * +_curses_window_noutrefresh_impl(PyCursesWindowObject *self) +/*[clinic end generated code: output=6ef6dec666643fee input=876902e3fa431dbd]*/ +#endif { int rtn; +#ifdef py_is_pad if (py_is_pad(self->win)) { if (!group_right_1) { PyErr_SetString(PyCursesError, @@ -2028,6 +2041,7 @@ _curses_window_noutrefresh_impl(PyCursesWindowObject *self, "noutrefresh() takes no arguments (6 given)"); return NULL; } +#endif Py_BEGIN_ALLOW_THREADS rtn = wnoutrefresh(self->win); Py_END_ALLOW_THREADS @@ -2232,6 +2246,7 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, { int rtn; +#ifdef py_is_pad if (py_is_pad(self->win)) { if (!group_right_1) { PyErr_SetString(PyCursesError, @@ -2244,6 +2259,7 @@ _curses_window_refresh_impl(PyCursesWindowObject *self, int group_right_1, Py_END_ALLOW_THREADS return PyCursesCheckERR(rtn, "prefresh"); } +#endif if (group_right_1) { PyErr_SetString(PyExc_TypeError, "refresh() takes no arguments (6 given)"); @@ -2306,11 +2322,13 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1, WINDOW *win; /* printf("Subwin: %i %i %i %i \n", nlines, ncols, begin_y, begin_x); */ +#ifdef py_is_pad if (py_is_pad(self->win)) { win = subpad(self->win, nlines, ncols, begin_y, begin_x); - } else { - win = subwin(self->win, nlines, ncols, begin_y, begin_x); } + else +#endif + win = subwin(self->win, nlines, ncols, begin_y, begin_x); if (win == NULL) { PyErr_SetString(PyCursesError, catchall_NULL); diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index 12aebac0689ff5..f7e0aaf7b23649 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -1274,6 +1274,8 @@ _curses_window_is_linetouched(PyCursesWindowObject *self, PyObject *arg) return return_value; } +#if defined(py_is_pad) + PyDoc_STRVAR(_curses_window_noutrefresh__doc__, "noutrefresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])\n" "Mark for refresh but wait.\n" @@ -1322,6 +1324,34 @@ _curses_window_noutrefresh(PyCursesWindowObject *self, PyObject *args) return return_value; } +#endif /* defined(py_is_pad) */ + +#if !defined(py_is_pad) + +PyDoc_STRVAR(_curses_window_noutrefresh__doc__, +"noutrefresh($self, /)\n" +"--\n" +"\n" +"Mark for refresh but wait.\n" +"\n" +"This function updates the data structure representing the desired state of the\n" +"window, but does not force an update of the physical screen. To accomplish\n" +"that, call doupdate()."); + +#define _CURSES_WINDOW_NOUTREFRESH_METHODDEF \ + {"noutrefresh", (PyCFunction)_curses_window_noutrefresh, METH_NOARGS, _curses_window_noutrefresh__doc__}, + +static PyObject * +_curses_window_noutrefresh_impl(PyCursesWindowObject *self); + +static PyObject * +_curses_window_noutrefresh(PyCursesWindowObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _curses_window_noutrefresh_impl(self); +} + +#endif /* !defined(py_is_pad) */ + PyDoc_STRVAR(_curses_window_overlay__doc__, "overlay(destwin, [sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol])\n" "Overlay the window on top of destwin.\n" @@ -4205,6 +4235,10 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #define _CURSES_WINDOW_GET_WCH_METHODDEF #endif /* !defined(_CURSES_WINDOW_GET_WCH_METHODDEF) */ +#ifndef _CURSES_WINDOW_NOUTREFRESH_METHODDEF + #define _CURSES_WINDOW_NOUTREFRESH_METHODDEF +#endif /* !defined(_CURSES_WINDOW_NOUTREFRESH_METHODDEF) */ + #ifndef _CURSES_FILTER_METHODDEF #define _CURSES_FILTER_METHODDEF #endif /* !defined(_CURSES_FILTER_METHODDEF) */ @@ -4284,4 +4318,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF #define _CURSES_USE_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=2ac38c77c97552ad input=a9049054013a1b77]*/ +/*[clinic end generated code: output=96887782374f070a input=a9049054013a1b77]*/ diff --git a/configure.ac b/configure.ac index 53ce92bfd1cca7..720c346fda6825 100644 --- a/configure.ac +++ b/configure.ac @@ -6492,10 +6492,10 @@ AC_DEFUN([PY_CHECK_CURSES_FUNC], [py_var], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( - [[ + [ #define NCURSES_OPAQUE 0 #include - ]], [ + ], [ #ifndef $1 void *x=$1 #endif From d573579ba5df0621f70eed96e99db420793ba90e Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Tue, 13 Feb 2024 12:10:03 +0100 Subject: [PATCH 5/6] explain version check --- Include/py_curses.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Include/py_curses.h b/Include/py_curses.h index 46f060d192091d..de725a7b849f78 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -41,6 +41,8 @@ use , which has some or all these features. */ #if !defined(WINDOW_HAS_FLAGS) && \ (NCURSES_VERSION_PATCH+0 < 20070303 || !(NCURSES_OPAQUE+0)) +/* the WINDOW flags field was always accessible in ncurses prior to 20070303; + after that, it depends on the value of NCURSES_OPAQUE. */ #define WINDOW_HAS_FLAGS 1 #endif #if !defined(HAVE_CURSES_IS_PAD) && NCURSES_VERSION_PATCH+0 >= 20090906 From 7c94a461219dde5461bcdd7386376726f9383601 Mon Sep 17 00:00:00 2001 From: Davide Rizzo Date: Tue, 13 Feb 2024 12:24:41 +0100 Subject: [PATCH 6/6] restrict NCURSES_OPAQUE check to Apple platforms --- Include/py_curses.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Include/py_curses.h b/Include/py_curses.h index de725a7b849f78..a51d9980eee401 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -23,10 +23,16 @@ # endif #endif -#if defined(WINDOW_HAS_FLAGS) -/* The following definition is necessary for ncurses 5.7; without it, - some of [n]curses.h set NCURSES_OPAQUE to 1, and then Python - can't get at the WINDOW flags field. */ +#if defined(WINDOW_HAS_FLAGS) && defined(__APPLE__) +/* gh-109617, gh-115383: we can rely on the default value for NCURSES_OPAQUE on + most platforms, but not on macOS. This is because, starting with Xcode 15, + Apple-provided ncurses.h comes from ncurses 6 (which defaults to opaque + structs) but can still be linked to older versions of ncurses dynamic + libraries which don't provide functions such as is_pad() to deal with opaque + structs. Setting NCURSES_OPAQUE to 0 is harmless in all ncurses releases to + this date (provided that a thread-safe implementation is not required), but + this might change in the future. This fix might become irrelevant once + support for macOS 13 or earlier is dropped. */ #define NCURSES_OPAQUE 0 #endif