Skip to content
Open
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
51 changes: 51 additions & 0 deletions Doc/library/curses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ The module :mod:`!curses` defines the following functions:
.. versionadded:: 3.14


.. function:: alloc_pair(fg, bg)

Allocate a color pair for foreground color *fg* and background color *bg*,
and return its number. If a color pair for the same combination of colors
already exists, return its number. Otherwise allocate a new color pair and
return its number.

This function is only available if Python was built against a wide-character
version of the underlying curses library with extended-color support (see
:func:`has_extended_color_support`).

.. versionadded:: next


.. function:: baudrate()

Return the output speed of the terminal in bits per second. On software
Expand Down Expand Up @@ -215,6 +229,19 @@ The module :mod:`!curses` defines the following functions:
.. versionadded:: next


.. function:: find_pair(fg, bg)

Return the number of a color pair for foreground color *fg* and background
color *bg*, or ``-1`` if no color pair for this combination of colors has
been allocated.

This function is only available if Python was built against a wide-character
version of the underlying curses library with extended-color support (see
:func:`has_extended_color_support`).

.. versionadded:: next


.. function:: flash()

Flash the screen. That is, change it to reverse-video and then change it back
Expand All @@ -228,6 +255,18 @@ The module :mod:`!curses` defines the following functions:
by the user and has not yet been processed by the program.


.. function:: free_pair(pair_number)

Free the color pair *pair_number*, which must have been allocated by
:func:`alloc_pair`. The pair must not be in use.

This function is only available if Python was built against a wide-character
version of the underlying curses library with extended-color support (see
:func:`has_extended_color_support`).

.. versionadded:: next


.. function:: getmouse()

After :meth:`~window.getch` returns :const:`KEY_MOUSE` to signal a mouse event, this
Expand Down Expand Up @@ -519,6 +558,18 @@ The module :mod:`!curses` defines the following functions:
presented to curses input functions one by one.


.. function:: reset_color_pairs()

Discard all color-pair definitions, releasing the color pairs allocated by
:func:`init_pair` and :func:`alloc_pair`.

This function is only available if Python was built against a wide-character
version of the underlying curses library with extended-color support (see
:func:`has_extended_color_support`).

.. versionadded:: next


.. function:: reset_prog_mode()

Restore the terminal to "program" mode, as previously saved by
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ curses
* Add :func:`curses.nofilter`, which undoes the effect of :func:`curses.filter`.
(Contributed by Serhiy Storchaka in :gh:`151744`.)

* Add the :mod:`curses` functions :func:`curses.alloc_pair`,
:func:`curses.find_pair`, :func:`curses.free_pair` and
:func:`curses.reset_color_pairs` for dynamic color-pair management,
available when built against a wide-character ncurses with extended-color
support.
(Contributed by Serhiy Storchaka in :gh:`151774`.)

gzip
----

Expand Down
48 changes: 48 additions & 0 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,54 @@ def test_init_pair(self):
self.assertRaises(ValueError, curses.init_pair, 1, color, 0)
self.assertRaises(ValueError, curses.init_pair, 1, 0, color)

@requires_curses_func('alloc_pair')
@requires_colors
def test_dynamic_color_pairs(self):
# alloc_pair()/find_pair()/free_pair() (extended-color extension).
fg = bg = curses.COLORS - 1
pair = curses.alloc_pair(fg, bg)
self.assertGreater(pair, 0)
self.assertEqual(curses.pair_content(pair), (fg, bg))
# The same combination of colors reuses the same pair.
self.assertEqual(curses.alloc_pair(fg, bg), pair)
self.assertEqual(curses.find_pair(fg, bg), pair)
# Once freed, the pair is no longer found.
self.assertIsNone(curses.free_pair(pair))
self.assertEqual(curses.find_pair(fg, bg), -1)

# Error paths.
for color in self.bad_colors2():
self.assertRaises(ValueError, curses.alloc_pair, color, 0)
self.assertRaises(ValueError, curses.alloc_pair, 0, color)
self.assertRaises(ValueError, curses.find_pair, color, 0)
self.assertRaises(ValueError, curses.find_pair, 0, color)
for pair in self.bad_pairs():
self.assertRaises(ValueError, curses.free_pair, pair)
# Color pair 0 is reserved and cannot be freed.
self.assertRaises(curses.error, curses.free_pair, 0)

# Invalid number or type of arguments.
self.assertRaises(TypeError, curses.alloc_pair)
self.assertRaises(TypeError, curses.alloc_pair, 0)
self.assertRaises(TypeError, curses.alloc_pair, 0, 0, 0)
self.assertRaises(TypeError, curses.alloc_pair, 'red', 0)
self.assertRaises(TypeError, curses.alloc_pair, 0, 'red')
self.assertRaises(TypeError, curses.alloc_pair, fg=0, bg=0)
self.assertRaises(TypeError, curses.find_pair)
self.assertRaises(TypeError, curses.find_pair, 0)
self.assertRaises(TypeError, curses.find_pair, 0, 0, 0)
self.assertRaises(TypeError, curses.find_pair, 'red', 0)
self.assertRaises(TypeError, curses.find_pair, 0, 'red')
self.assertRaises(TypeError, curses.free_pair)
self.assertRaises(TypeError, curses.free_pair, 1, 2)
self.assertRaises(TypeError, curses.free_pair, 'red')

@requires_curses_func('reset_color_pairs')
@requires_colors
def test_reset_color_pairs(self):
self.assertIsNone(curses.reset_color_pairs())
self.assertRaises(TypeError, curses.reset_color_pairs, 0)

@requires_colors
def test_color_attrs(self):
for pair in 0, 1, 255:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Add the :mod:`curses` functions :func:`curses.alloc_pair`,
:func:`curses.find_pair`, :func:`curses.free_pair` and
:func:`curses.reset_color_pairs` for dynamic color-pair management. They are
only available when Python is built against a wide-character version of the
underlying curses library with extended-color support.
98 changes: 98 additions & 0 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3825,6 +3825,100 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg)
Py_RETURN_NONE;
}

#if _NCURSES_EXTENDED_COLOR_FUNCS
/*[clinic input]
_curses.alloc_pair

fg: color_allow_default
Foreground color number.
bg: color_allow_default
Background color number.
/

Allocate a color pair for the given foreground and background colors.

If a color pair for the same colors already exists, return its number.
Otherwise allocate a new color pair and return its number.
[clinic start generated code]*/

static PyObject *
_curses_alloc_pair_impl(PyObject *module, int fg, int bg)
/*[clinic end generated code: output=6eb08cb643d4b5a2 input=b29bafd7b360fa35]*/
{
PyCursesStatefulInitialised(module);
PyCursesStatefulInitialisedColor(module);

int pair = alloc_pair(fg, bg);
if (pair < 0) {
curses_set_error(module, "alloc_pair", NULL);
return NULL;
}
return PyLong_FromLong(pair);
}

/*[clinic input]
_curses.find_pair

fg: color_allow_default
Foreground color number.
bg: color_allow_default
Background color number.
/

Return the number of a color pair for the given colors, or -1.

Return -1 if no color pair for this combination of foreground and
background colors has been allocated.
[clinic start generated code]*/

static PyObject *
_curses_find_pair_impl(PyObject *module, int fg, int bg)
/*[clinic end generated code: output=376026c2a3ac4a9b input=930feac14892c251]*/
{
PyCursesStatefulInitialised(module);
PyCursesStatefulInitialisedColor(module);

return PyLong_FromLong(find_pair(fg, bg));
}

/*[clinic input]
_curses.free_pair

pair: pair
The number of the color pair to free.
/

Free a color pair allocated by alloc_pair().
[clinic start generated code]*/

static PyObject *
_curses_free_pair_impl(PyObject *module, int pair)
/*[clinic end generated code: output=61be0fb2e4bb4e4a input=d24df62feb4161c6]*/
{
PyCursesStatefulInitialised(module);
PyCursesStatefulInitialisedColor(module);

return curses_check_err(module, free_pair(pair), "free_pair", NULL);
}

/*[clinic input]
_curses.reset_color_pairs

Discard all color-pair definitions.
[clinic start generated code]*/

static PyObject *
_curses_reset_color_pairs_impl(PyObject *module)
/*[clinic end generated code: output=117e68c6614e1d06 input=57c1cf7e5447e1ac]*/
{
PyCursesStatefulInitialised(module);
PyCursesStatefulInitialisedColor(module);

reset_color_pairs();
Py_RETURN_NONE;
}
#endif /* _NCURSES_EXTENDED_COLOR_FUNCS */

/* Refresh the private copy of the screen encoding from a freshly created
stdscr window object. Returns 0 on success, -1 with an exception set. */
static int
Expand Down Expand Up @@ -5328,6 +5422,7 @@ _curses_has_extended_color_support_impl(PyObject *module)
/* List of functions defined in the module */

static PyMethodDef cursesmodule_methods[] = {
_CURSES_ALLOC_PAIR_METHODDEF
_CURSES_BAUDRATE_METHODDEF
_CURSES_BEEP_METHODDEF
_CURSES_CAN_CHANGE_COLOR_METHODDEF
Expand All @@ -5344,8 +5439,10 @@ static PyMethodDef cursesmodule_methods[] = {
_CURSES_ERASECHAR_METHODDEF
_CURSES_FILTER_METHODDEF
_CURSES_NOFILTER_METHODDEF
_CURSES_FIND_PAIR_METHODDEF
_CURSES_FLASH_METHODDEF
_CURSES_FLUSHINP_METHODDEF
_CURSES_FREE_PAIR_METHODDEF
_CURSES_GETMOUSE_METHODDEF
_CURSES_UNGETMOUSE_METHODDEF
_CURSES_GETSYX_METHODDEF
Expand Down Expand Up @@ -5382,6 +5479,7 @@ static PyMethodDef cursesmodule_methods[] = {
_CURSES_PUTP_METHODDEF
_CURSES_QIFLUSH_METHODDEF
_CURSES_RAW_METHODDEF
_CURSES_RESET_COLOR_PAIRS_METHODDEF
_CURSES_RESET_PROG_MODE_METHODDEF
_CURSES_RESET_SHELL_MODE_METHODDEF
_CURSES_RESETTY_METHODDEF
Expand Down
Loading
Loading