]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-152502: Detect the curses mouse interface portably (GH-152705) (GH-152707)
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 30 Jun 2026 20:59:28 +0000 (23:59 +0300)
committerGitHub <noreply@github.com>
Tue, 30 Jun 2026 20:59:28 +0000 (23:59 +0300)
The mouse interface (getmouse(), the BUTTON* constants, ...) was gated on the
ncurses-specific NCURSES_MOUSE_VERSION macro, so it was dropped on other curses
implementations that provide it, such as NetBSD curses and PDCurses.

Gate it instead on a configure capability probe or the PDCURSES macro.  Probe
for getmouse() with its X/Open getmouse(MEVENT *) signature, since PDCurses
declares an incompatible getmouse(void) unless built for the ncurses mouse API,
which PDC_NCMOUSE now always selects.

(cherry picked from commit 7bbea4f4868ef89b07b986d7a0d4b585e8271f27)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Include/py_curses.h
Misc/NEWS.d/next/Library/2026-06-30-21-40-00.gh-issue-152502.Kq3Vn7.rst [new file with mode: 0644]
Modules/_cursesmodule.c
Modules/clinic/_cursesmodule.c.h
configure
configure.ac
pyconfig.h.in

index 0948aabedd499398c26a2612e48d6a7f7bbd6a2f..1127f1ea263e4078ecb6e113c384ad59bcf2d3b5 100644 (file)
 #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 <ncursesw/ncurses.h>
 #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 (file)
index 0000000..815bc47
--- /dev/null
@@ -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``).
index f474174ef7c9ddbcacf398f28677c1a60bef5fb0..dd5af63fcc8a4665e8ae937d9c87a8380c6fda15 100644 (file)
@@ -1681,7 +1681,7 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch,
     return curses_window_check_err(self, rtn, funcname, "echochar");
 }
 
-#ifdef NCURSES_MOUSE_VERSION
+#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)
 /*[clinic input]
 @permit_long_summary
 _curses.window.enclose
@@ -3532,7 +3532,7 @@ _curses_getsyx_impl(PyObject *module)
 }
 #endif
 
-#ifdef NCURSES_MOUSE_VERSION
+#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)
 /*[clinic input]
 _curses.getmouse
 
@@ -4253,7 +4253,7 @@ _curses_meta_impl(PyObject *module, int yes)
     return curses_check_err(module, meta(stdscr, yes), "meta", NULL);
 }
 
-#ifdef NCURSES_MOUSE_VERSION
+#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)
 /*[clinic input]
 _curses.mouseinterval
 
@@ -5685,7 +5685,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);
@@ -5711,7 +5711,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);
index 9235586587e8761ba25a2b254bb0d761edfb880c..0d2237241260eb6b3548e4c73dcde6c5c8cea560 100644 (file)
@@ -683,7 +683,7 @@ exit:
     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 @@ exit:
     return return_value;
 }
 
-#endif /* defined(NCURSES_MOUSE_VERSION) */
+#endif /* (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) */
 
 PyDoc_STRVAR(_curses_window_getbkgd__doc__,
 "getbkgd($self, /)\n"
@@ -2307,7 +2307,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"
@@ -2330,9 +2330,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 @@ exit:
     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 @@ exit:
     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 @@ exit:
     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"
@@ -3206,7 +3206,7 @@ exit:
     return return_value;
 }
 
-#endif /* defined(NCURSES_MOUSE_VERSION) */
+#endif /* (defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)) */
 
 PyDoc_STRVAR(_curses_napms__doc__,
 "napms($module, ms, /)\n"
@@ -4486,4 +4486,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=7835b44ce9413f7f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c1b7520d331d3d61 input=a9049054013a1b77]*/
index d17240882ec956b0d1b2692cb90998f46a6963d8..b24bb35148beee1c06ae3ef49085e70ad42e00bd 100755 (executable)
--- a/configure
+++ b/configure
 
 
 
+
+{ 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 <ncursesw/ncurses.h>
+#elif defined(HAVE_NCURSESW_CURSES_H)
+#  include <ncursesw/curses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+#  include <ncurses/ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+#  include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+#  include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+#  include <curses.h>
+#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
index 0cf3d3f24506fcfb5c275cc7487bbe9ce923f2e3..09a985dc9ce0d7dfd28b0a2415cbd205a33c2231 100644 (file)
@@ -7287,6 +7287,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
index 058fc9f0a42f5e508361f2e466591053d4addd97..618e62bea0c26ceff469a54c8e158b427fabe954 100644 (file)
 /* 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 <curses.h> header file. */
 #undef HAVE_CURSES_H