Skip to content

Commit 83391ce

Browse files
[3.15] gh-152502: Detect the curses mouse interface portably (GH-152705) (GH-152707)
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 7bbea4f) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e9d064f commit 83391ce

7 files changed

Lines changed: 94 additions & 16 deletions

File tree

Include/py_curses.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@
3636
#define NCURSES_OPAQUE 0
3737
#endif
3838

39+
/* PDCurses exposes its ncurses-compatible mouse API, the one this module uses,
40+
only when this is defined before the curses header is included below.
41+
Ignored by other curses implementations. */
42+
#ifndef PDC_NCMOUSE
43+
# define PDC_NCMOUSE
44+
#endif
45+
3946
#if defined(HAVE_NCURSESW_NCURSES_H)
4047
# include <ncursesw/ncurses.h>
4148
#elif defined(HAVE_NCURSESW_CURSES_H)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Detect the :mod:`curses` mouse interface (:func:`~curses.getmouse`, the
2+
``BUTTON*`` constants, and others) with a configure capability probe or library
3+
macros instead of gating it on ncurses-specific macros. It is now also
4+
available with other curses implementations that provide it, such as NetBSD
5+
curses and PDCurses (the latter underpins ``windows-curses``).

Modules/_cursesmodule.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,7 +1681,7 @@ _curses_window_echochar_impl(PyCursesWindowObject *self, PyObject *ch,
16811681
return curses_window_check_err(self, rtn, funcname, "echochar");
16821682
}
16831683

1684-
#ifdef NCURSES_MOUSE_VERSION
1684+
#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)
16851685
/*[clinic input]
16861686
@permit_long_summary
16871687
_curses.window.enclose
@@ -3532,7 +3532,7 @@ _curses_getsyx_impl(PyObject *module)
35323532
}
35333533
#endif
35343534

3535-
#ifdef NCURSES_MOUSE_VERSION
3535+
#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)
35363536
/*[clinic input]
35373537
_curses.getmouse
35383538
@@ -4253,7 +4253,7 @@ _curses_meta_impl(PyObject *module, int yes)
42534253
return curses_check_err(module, meta(stdscr, yes), "meta", NULL);
42544254
}
42554255

4256-
#ifdef NCURSES_MOUSE_VERSION
4256+
#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)
42574257
/*[clinic input]
42584258
_curses.mouseinterval
42594259
@@ -5685,7 +5685,7 @@ cursesmodule_exec(PyObject *module)
56855685
SetDictInt("COLOR_CYAN", COLOR_CYAN);
56865686
SetDictInt("COLOR_WHITE", COLOR_WHITE);
56875687

5688-
#ifdef NCURSES_MOUSE_VERSION
5688+
#if defined(HAVE_CURSES_GETMOUSE) || defined(PDCURSES)
56895689
/* Mouse-related constants */
56905690
SetDictInt("BUTTON1_PRESSED", BUTTON1_PRESSED);
56915691
SetDictInt("BUTTON1_RELEASED", BUTTON1_RELEASED);
@@ -5711,7 +5711,7 @@ cursesmodule_exec(PyObject *module)
57115711
SetDictInt("BUTTON4_DOUBLE_CLICKED", BUTTON4_DOUBLE_CLICKED);
57125712
SetDictInt("BUTTON4_TRIPLE_CLICKED", BUTTON4_TRIPLE_CLICKED);
57135713

5714-
#if NCURSES_MOUSE_VERSION > 1
5714+
#ifdef BUTTON5_PRESSED
57155715
SetDictInt("BUTTON5_PRESSED", BUTTON5_PRESSED);
57165716
SetDictInt("BUTTON5_RELEASED", BUTTON5_RELEASED);
57175717
SetDictInt("BUTTON5_CLICKED", BUTTON5_CLICKED);

Modules/clinic/_cursesmodule.c.h

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7287,6 +7287,18 @@ PY_CHECK_CURSES_FUNC([set_escdelay])
72877287
PY_CHECK_CURSES_FUNC([set_tabsize])
72887288
PY_CHECK_CURSES_VAR([ESCDELAY])
72897289
PY_CHECK_CURSES_VAR([TABSIZE])
7290+
7291+
dnl Probe for the X/Open getmouse(MEVENT *) signature specifically: PDCurses
7292+
dnl declares an incompatible getmouse(void) unless built for the ncurses mouse API.
7293+
AC_CACHE_CHECK([for ncurses-style curses function getmouse],
7294+
[ac_cv_lib_curses_getmouse],
7295+
[AC_COMPILE_IFELSE(
7296+
[AC_LANG_PROGRAM(_CURSES_INCLUDES, [MEVENT event; (void)getmouse(&event);])],
7297+
[ac_cv_lib_curses_getmouse=yes],
7298+
[ac_cv_lib_curses_getmouse=no])])
7299+
AS_VAR_IF([ac_cv_lib_curses_getmouse], [yes],
7300+
[AC_DEFINE([HAVE_CURSES_GETMOUSE], [1],
7301+
[Define if you have the 'getmouse' function with the X/Open signature.])])
72907302
CPPFLAGS=$ac_save_cppflags
72917303
])dnl have_curses != no
72927304
])dnl save env

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@
191191
/* Define if you have the 'filter' function. */
192192
#undef HAVE_CURSES_FILTER
193193

194+
/* Define if you have the 'getmouse' function with the X/Open signature. */
195+
#undef HAVE_CURSES_GETMOUSE
196+
194197
/* Define to 1 if you have the <curses.h> header file. */
195198
#undef HAVE_CURSES_H
196199

0 commit comments

Comments
 (0)