Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Doc/library/curses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,13 @@ The module :mod:`!curses` defines the following functions:
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
Expand Down Expand Up @@ -1309,6 +1316,18 @@ Window objects
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).
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ curses
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.
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,22 @@ def test_enclose(self):
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')
Expand Down Expand Up @@ -1824,6 +1840,11 @@ def test_has_colors(self):
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')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add the :func:`curses.has_mouse` function and the
:meth:`curses.window.mouse_trafo` method.
49 changes: 48 additions & 1 deletion Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
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
Expand Down Expand Up @@ -3007,6 +3007,36 @@ _curses_window_enclose_impl(PyCursesWindowObject *self, int y, int x)
{
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]
Expand Down Expand Up @@ -4836,6 +4866,7 @@ static PyMethodDef PyCursesWindow_methods[] = {
_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."},
Expand Down Expand Up @@ -6995,6 +7026,21 @@ _curses_meta_impl(PyObject *module, int yes)
}

#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

Expand Down Expand Up @@ -8204,6 +8250,7 @@ static PyMethodDef cursesmodule_methods[] = {
_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
Expand Down
89 changes: 88 additions & 1 deletion Modules/clinic/_cursesmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading