has_mouse() reports whether the mouse driver was successfully initialized.
window.mouse_trafo(y, x, to_screen) converts a coordinate pair between
window-relative and screen-relative coordinates, returning the (y, x) pair or
None if it lies outside the window. Together these complete the curses mouse
interface.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
a key with that value.
+.. function:: has_mouse()
+
+ Return ``True`` if the mouse driver has been successfully initialized.
+
+ .. versionadded:: next
+
+
.. function:: define_key(definition, keycode)
Define an escape sequence *definition*, a string, as a key that generates
Previously it returned ``1`` or ``0`` instead of ``True`` or ``False``.
+.. method:: window.mouse_trafo(y, x, to_screen)
+
+ Convert between window-relative and screen-relative (``stdscr``-relative) character-cell coordinates.
+ If *to_screen* is true, convert the window-relative coordinates *y*, *x* to screen-relative coordinates;
+ otherwise convert in the opposite direction.
+ The two coordinate systems differ when lines are reserved on the screen, for example for soft labels.
+
+ Return the converted coordinates as a ``(y, x)`` tuple, or ``None`` if they lie outside the window.
+
+ .. versionadded:: next
+
+
.. attribute:: window.encoding
Encoding used to encode method arguments (Unicode strings and characters).
against an ncurses with ``NCURSES_EXT_FUNCS``.
(Contributed by Serhiy Storchaka in :gh:`152334`.)
+* Add the :func:`curses.has_mouse` function and the
+ :meth:`curses.window.mouse_trafo` method, completing the :mod:`curses`
+ mouse interface.
+ (Contributed by Serhiy Storchaka in :gh:`152325`.)
+
* :class:`curses.textpad.Textbox` now supports entering and reading back the
full Unicode range, including combining characters, when curses is built with
wide-character support.
self.assertIs(win.enclose(7, 19), False)
self.assertIs(win.enclose(6, 20), False)
+ @requires_curses_window_meth('mouse_trafo')
+ def test_mouse_trafo(self):
+ win = curses.newwin(5, 15, 2, 5)
+ # to_screen=True: window-relative -> stdscr-relative.
+ self.assertEqual(win.mouse_trafo(0, 0, True), (2, 5))
+ self.assertEqual(win.mouse_trafo(3, 10, True), (5, 15))
+ self.assertEqual(win.mouse_trafo(4, 14, True), (6, 19))
+ # A coordinate outside the window has no counterpart.
+ self.assertIsNone(win.mouse_trafo(5, 0, True))
+ self.assertIsNone(win.mouse_trafo(0, 15, True))
+ # to_screen=False is the inverse: stdscr-relative -> window-relative.
+ self.assertEqual(win.mouse_trafo(2, 5, False), (0, 0))
+ self.assertEqual(win.mouse_trafo(6, 19, False), (4, 14))
+ self.assertIsNone(win.mouse_trafo(1, 5, False))
+ self.assertIsNone(win.mouse_trafo(7, 19, False))
+
def test_putwin(self):
win = curses.newwin(5, 12, 1, 2)
win.addstr(2, 1, 'Lorem ipsum')
self.assertIsInstance(curses.has_colors(), bool)
self.assertIsInstance(curses.can_change_color(), bool)
+ @requires_curses_func('has_mouse')
+ def test_has_mouse(self):
+ # Whether a mouse is available depends on the terminal.
+ self.assertIsInstance(curses.has_mouse(), bool)
+
def test_start_color(self):
if not curses.has_colors():
self.skipTest('requires colors support')
--- /dev/null
+Add the :func:`curses.has_mouse` function and the
+:meth:`curses.window.mouse_trafo` method.
del_curterm mcprint mvcur restartterm
ripoffline set_curterm setterm
tgetent tgetflag tgetnum tgetstr tgoto tputs
- vidattr vidputs wmouse_trafo
+ vidattr vidputs
Low-priority:
slk_attr slk_attr_off slk_attr_on slk_attr_set slk_attroff
{
return PyBool_FromLong(wenclose(self->win, y, x));
}
+
+/*[clinic input]
+_curses.window.mouse_trafo
+
+ y: int
+ Y-coordinate.
+ x: int
+ X-coordinate.
+ to_screen: bool
+ If True, convert window-relative coordinates to
+ stdscr-relative ones; otherwise convert the other way.
+ /
+
+Convert coordinates between window-relative and screen-relative.
+
+Return the converted (y, x) coordinates, or None if they are
+outside the window.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_window_mouse_trafo_impl(PyCursesWindowObject *self, int y, int x,
+ int to_screen)
+/*[clinic end generated code: output=b21572fa3524c15d input=c51fd793af7f6965]*/
+{
+ int ry = y, rx = x;
+ if (!wmouse_trafo(self->win, &ry, &rx, to_screen)) {
+ Py_RETURN_NONE;
+ }
+ return Py_BuildValue("(ii)", ry, rx);
+}
#endif
/*[clinic input]
_CURSES_WINDOW_DUPWIN_METHODDEF
_CURSES_WINDOW_ECHOCHAR_METHODDEF
_CURSES_WINDOW_ENCLOSE_METHODDEF
+ _CURSES_WINDOW_MOUSE_TRAFO_METHODDEF
{"erase", PyCursesWindow_werase, METH_NOARGS,
"erase($self, /)\n--\n\n"
"Clear the window."},
}
#ifdef NCURSES_MOUSE_VERSION
+/*[clinic input]
+_curses.has_mouse
+
+Return True if the mouse driver has been successfully initialized.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_has_mouse_impl(PyObject *module)
+/*[clinic end generated code: output=7901cc34069e4f57 input=94682101a11c4f30]*/
+{
+ PyCursesStatefulInitialised(module);
+
+ return PyBool_FromLong(has_mouse());
+}
+
/*[clinic input]
_curses.mouseinterval
_CURSES_HAS_IC_METHODDEF
_CURSES_HAS_IL_METHODDEF
_CURSES_HAS_KEY_METHODDEF
+ _CURSES_HAS_MOUSE_METHODDEF
_CURSES_DEFINE_KEY_METHODDEF
_CURSES_KEY_DEFINED_METHODDEF
_CURSES_KEYOK_METHODDEF
#endif /* defined(NCURSES_MOUSE_VERSION) */
+#if defined(NCURSES_MOUSE_VERSION)
+
+PyDoc_STRVAR(_curses_window_mouse_trafo__doc__,
+"mouse_trafo($self, y, x, to_screen, /)\n"
+"--\n"
+"\n"
+"Convert coordinates between window-relative and screen-relative.\n"
+"\n"
+" y\n"
+" Y-coordinate.\n"
+" x\n"
+" X-coordinate.\n"
+" to_screen\n"
+" If True, convert window-relative coordinates to\n"
+" stdscr-relative ones; otherwise convert the other way.\n"
+"\n"
+"Return the converted (y, x) coordinates, or None if they are\n"
+"outside the window.");
+
+#define _CURSES_WINDOW_MOUSE_TRAFO_METHODDEF \
+ {"mouse_trafo", _PyCFunction_CAST(_curses_window_mouse_trafo), METH_FASTCALL, _curses_window_mouse_trafo__doc__},
+
+static PyObject *
+_curses_window_mouse_trafo_impl(PyCursesWindowObject *self, int y, int x,
+ int to_screen);
+
+static PyObject *
+_curses_window_mouse_trafo(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ int y;
+ int x;
+ int to_screen;
+
+ if (!_PyArg_CheckPositional("mouse_trafo", nargs, 3, 3)) {
+ goto exit;
+ }
+ y = PyLong_AsInt(args[0]);
+ if (y == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ x = PyLong_AsInt(args[1]);
+ if (x == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ to_screen = PyObject_IsTrue(args[2]);
+ if (to_screen < 0) {
+ goto exit;
+ }
+ return_value = _curses_window_mouse_trafo_impl((PyCursesWindowObject *)self, y, x, to_screen);
+
+exit:
+ return return_value;
+}
+
+#endif /* defined(NCURSES_MOUSE_VERSION) */
+
PyDoc_STRVAR(_curses_window_getbkgd__doc__,
"getbkgd($self, /)\n"
"--\n"
#if defined(NCURSES_MOUSE_VERSION)
+PyDoc_STRVAR(_curses_has_mouse__doc__,
+"has_mouse($module, /)\n"
+"--\n"
+"\n"
+"Return True if the mouse driver has been successfully initialized.");
+
+#define _CURSES_HAS_MOUSE_METHODDEF \
+ {"has_mouse", (PyCFunction)_curses_has_mouse, METH_NOARGS, _curses_has_mouse__doc__},
+
+static PyObject *
+_curses_has_mouse_impl(PyObject *module);
+
+static PyObject *
+_curses_has_mouse(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _curses_has_mouse_impl(module);
+}
+
+#endif /* defined(NCURSES_MOUSE_VERSION) */
+
+#if defined(NCURSES_MOUSE_VERSION)
+
PyDoc_STRVAR(_curses_mouseinterval__doc__,
"mouseinterval($module, interval, /)\n"
"--\n"
#define _CURSES_WINDOW_ENCLOSE_METHODDEF
#endif /* !defined(_CURSES_WINDOW_ENCLOSE_METHODDEF) */
+#ifndef _CURSES_WINDOW_MOUSE_TRAFO_METHODDEF
+ #define _CURSES_WINDOW_MOUSE_TRAFO_METHODDEF
+#endif /* !defined(_CURSES_WINDOW_MOUSE_TRAFO_METHODDEF) */
+
#ifndef _CURSES_WINDOW_NOUTREFRESH_METHODDEF
#define _CURSES_WINDOW_NOUTREFRESH_METHODDEF
#endif /* !defined(_CURSES_WINDOW_NOUTREFRESH_METHODDEF) */
#define _CURSES_IS_TERM_RESIZED_METHODDEF
#endif /* !defined(_CURSES_IS_TERM_RESIZED_METHODDEF) */
+#ifndef _CURSES_HAS_MOUSE_METHODDEF
+ #define _CURSES_HAS_MOUSE_METHODDEF
+#endif /* !defined(_CURSES_HAS_MOUSE_METHODDEF) */
+
#ifndef _CURSES_MOUSEINTERVAL_METHODDEF
#define _CURSES_MOUSEINTERVAL_METHODDEF
#endif /* !defined(_CURSES_MOUSEINTERVAL_METHODDEF) */
#ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
#define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF
#endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */
-/*[clinic end generated code: output=f48f8e3554b30b86 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=09d21a41a5bd86dc input=a9049054013a1b77]*/