diff --git a/Include/py_curses.h b/Include/py_curses.h index 49fc3c9d127aa6f..3443ff7d54fc8f0 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -36,6 +36,13 @@ #define NCURSES_OPAQUE 0 #endif +/* PDCurses exposes its ncurses-compatible mouse API, the one this module uses, + only when this is defined before the curses header is included below. + Ignored by other curses implementations. */ +#ifndef PDC_NCMOUSE +# define PDC_NCMOUSE +#endif + #if defined(HAVE_NCURSESW_NCURSES_H) # include #elif defined(HAVE_NCURSESW_CURSES_H) diff --git a/Misc/NEWS.d/next/Library/2026-06-30-21-40-00.gh-issue-152502.Kq3Vn7.rst b/Misc/NEWS.d/next/Library/2026-06-30-21-40-00.gh-issue-152502.Kq3Vn7.rst new file mode 100644 index 000000000000000..815bc47966c40c3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-30-21-40-00.gh-issue-152502.Kq3Vn7.rst @@ -0,0 +1,5 @@ +Detect the :mod:`curses` mouse interface (:func:`~curses.getmouse`, the +``BUTTON*`` constants, and others) with a configure capability probe or library +macros instead of gating it on ncurses-specific macros. It is now also +available with other curses implementations that provide it, such as NetBSD +curses and PDCurses (the latter underpins ``windows-curses``). diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 52f813406268eee..51c4854aa5da52d 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -1502,7 +1502,7 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch, "echochar"); } -#ifdef NCURSES_MOUSE_VERSION +#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES) /*[clinic input] _curses.window.enclose @@ -3244,7 +3244,7 @@ _curses_getsyx_impl(PyObject *module) } #endif -#ifdef NCURSES_MOUSE_VERSION +#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES) /*[clinic input] _curses.getmouse @@ -3961,7 +3961,7 @@ _curses_meta_impl(PyObject *module, int yes) return PyCursesCheckERR(module, meta(stdscr, yes), "meta"); } -#ifdef NCURSES_MOUSE_VERSION +#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES) /*[clinic input] _curses.mouseinterval @@ -5385,7 +5385,7 @@ cursesmodule_exec(PyObject *module) SetDictInt("COLOR_CYAN", COLOR_CYAN); SetDictInt("COLOR_WHITE", COLOR_WHITE); -#ifdef NCURSES_MOUSE_VERSION +#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES) /* Mouse-related constants */ SetDictInt("BUTTON1_PRESSED", BUTTON1_PRESSED); SetDictInt("BUTTON1_RELEASED", BUTTON1_RELEASED); @@ -5411,7 +5411,7 @@ cursesmodule_exec(PyObject *module) SetDictInt("BUTTON4_DOUBLE_CLICKED", BUTTON4_DOUBLE_CLICKED); SetDictInt("BUTTON4_TRIPLE_CLICKED", BUTTON4_TRIPLE_CLICKED); -#if NCURSES_MOUSE_VERSION > 1 +#ifdef BUTTON5_PRESSED SetDictInt("BUTTON5_PRESSED", BUTTON5_PRESSED); SetDictInt("BUTTON5_RELEASED", BUTTON5_RELEASED); SetDictInt("BUTTON5_CLICKED", BUTTON5_CLICKED); diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index d433e9cfc0cf278..c1916d6e0d8e1b6 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -683,7 +683,7 @@ _curses_window_echochar(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#if defined(NCURSES_MOUSE_VERSION) +#if (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) PyDoc_STRVAR(_curses_window_enclose__doc__, "enclose($self, y, x, /)\n" @@ -726,7 +726,7 @@ _curses_window_enclose(PyObject *self, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* defined(NCURSES_MOUSE_VERSION) */ +#endif /* (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) */ PyDoc_STRVAR(_curses_window_getbkgd__doc__, "getbkgd($self, /)\n" @@ -2322,7 +2322,7 @@ _curses_getsyx(PyObject *module, PyObject *Py_UNUSED(ignored)) #endif /* defined(getsyx) */ -#if defined(NCURSES_MOUSE_VERSION) +#if (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) PyDoc_STRVAR(_curses_getmouse__doc__, "getmouse($module, /)\n" @@ -2345,9 +2345,9 @@ _curses_getmouse(PyObject *module, PyObject *Py_UNUSED(ignored)) return _curses_getmouse_impl(module); } -#endif /* defined(NCURSES_MOUSE_VERSION) */ +#endif /* (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) */ -#if defined(NCURSES_MOUSE_VERSION) +#if (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) PyDoc_STRVAR(_curses_ungetmouse__doc__, "ungetmouse($module, id, x, y, z, bstate, /)\n" @@ -2419,7 +2419,7 @@ _curses_ungetmouse(PyObject *module, PyObject *const *args, Py_ssize_t nargs) return return_value; } -#endif /* defined(NCURSES_MOUSE_VERSION) */ +#endif /* (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) */ PyDoc_STRVAR(_curses_getwin__doc__, "getwin($module, file, /)\n" @@ -3116,7 +3116,7 @@ _curses_meta(PyObject *module, PyObject *arg) return return_value; } -#if defined(NCURSES_MOUSE_VERSION) +#if (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) PyDoc_STRVAR(_curses_mouseinterval__doc__, "mouseinterval($module, interval, /)\n" @@ -3153,9 +3153,9 @@ _curses_mouseinterval(PyObject *module, PyObject *arg) return return_value; } -#endif /* defined(NCURSES_MOUSE_VERSION) */ +#endif /* (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) */ -#if defined(NCURSES_MOUSE_VERSION) +#if (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) PyDoc_STRVAR(_curses_mousemask__doc__, "mousemask($module, newmask, /)\n" @@ -3191,7 +3191,7 @@ _curses_mousemask(PyObject *module, PyObject *arg) return return_value; } -#endif /* defined(NCURSES_MOUSE_VERSION) */ +#endif /* (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) */ PyDoc_STRVAR(_curses_napms__doc__, "napms($module, ms, /)\n" @@ -4471,4 +4471,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=728a704064bc8b85 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=945fcc1c41f2ffe7 input=a9049054013a1b77]*/ diff --git a/configure b/configure index dac448e91f18cc9..8c6bf8b3cfe4a9b 100755 --- a/configure +++ b/configure @@ -29966,6 +29966,57 @@ fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ncurses-style curses function getmouse" >&5 +printf %s "checking for ncurses-style curses function getmouse... " >&6; } +if test ${ac_cv_lib_curses_getmouse+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#define NCURSES_OPAQUE 0 +#if defined(HAVE_NCURSESW_NCURSES_H) +# include +#elif defined(HAVE_NCURSESW_CURSES_H) +# include +#elif defined(HAVE_NCURSES_NCURSES_H) +# include +#elif defined(HAVE_NCURSES_CURSES_H) +# include +#elif defined(HAVE_NCURSES_H) +# include +#elif defined(HAVE_CURSES_H) +# include +#endif + +int +main (void) +{ +MEVENT event; (void)getmouse(&event); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_lib_curses_getmouse=yes +else case e in #( + e) ac_cv_lib_curses_getmouse=no ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_getmouse" >&5 +printf "%s\n" "$ac_cv_lib_curses_getmouse" >&6; } +if test "x$ac_cv_lib_curses_getmouse" = xyes +then : + +printf "%s\n" "#define HAVE_CURSES_GETMOUSE 1" >>confdefs.h + +fi CPPFLAGS=$ac_save_cppflags fi diff --git a/configure.ac b/configure.ac index d4a3bd417935f22..a65c16a05b15805 100644 --- a/configure.ac +++ b/configure.ac @@ -7086,6 +7086,18 @@ PY_CHECK_CURSES_FUNC([set_escdelay]) PY_CHECK_CURSES_FUNC([set_tabsize]) PY_CHECK_CURSES_VAR([ESCDELAY]) PY_CHECK_CURSES_VAR([TABSIZE]) + +dnl Probe for the X/Open getmouse(MEVENT *) signature specifically: PDCurses +dnl declares an incompatible getmouse(void) unless built for the ncurses mouse API. +AC_CACHE_CHECK([for ncurses-style curses function getmouse], + [ac_cv_lib_curses_getmouse], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM(_CURSES_INCLUDES, [MEVENT event; (void)getmouse(&event);])], + [ac_cv_lib_curses_getmouse=yes], + [ac_cv_lib_curses_getmouse=no])]) +AS_VAR_IF([ac_cv_lib_curses_getmouse], [yes], + [AC_DEFINE([HAVE_CURSES_GETMOUSE], [1], + [Define if you have the 'getmouse' function with the X/Open signature.])]) CPPFLAGS=$ac_save_cppflags ])dnl have_curses != no ])dnl save env diff --git a/pyconfig.h.in b/pyconfig.h.in index 202b49cd22a297c..a7e18d42c2a604d 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -192,6 +192,9 @@ /* Define if you have the 'filter' function. */ #undef HAVE_CURSES_FILTER +/* Define if you have the 'getmouse' function with the X/Open signature. */ +#undef HAVE_CURSES_GETMOUSE + /* Define to 1 if you have the header file. */ #undef HAVE_CURSES_H