]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-152263: Add curses soft-label-key functions (GH-152264)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 29 Jun 2026 17:57:56 +0000 (20:57 +0300)
committerGitHub <noreply@github.com>
Mon, 29 Jun 2026 17:57:56 +0000 (20:57 +0300)
Wrap the X/Open Curses soft-label-key functions: slk_init, slk_set,
slk_label, slk_refresh, slk_noutrefresh, slk_clear, slk_restore,
slk_touch, the chtype attribute functions slk_attron, slk_attroff,
slk_attrset and slk_attr, and the attr_t functions slk_attr_on,
slk_attr_off, slk_attr_set and slk_color.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Doc/library/curses.rst
Doc/whatsnew/3.16.rst
Lib/test/test_curses.py
Misc/NEWS.d/next/Library/2026-06-26-14-34-28.gh-issue-152263.5PHQP4.rst [new file with mode: 0644]
Modules/_cursesmodule.c
Modules/clinic/_cursesmodule.c.h

index 76967b88482cfcd9116f64c87680eed0cceb30c7..b3341fb8629f4cad91587aff926c3c262963f40a 100644 (file)
@@ -653,6 +653,131 @@ Windows and pads
    is to be displayed.
 
 
+Soft labels
+~~~~~~~~~~~
+
+.. _curses-slk:
+
+The following functions manage *soft-label keys*, a row of labels displayed
+along the bottom line of the screen, typically used to label a row of function
+keys.  :func:`slk_init` must be called before :func:`initscr` or
+:func:`newterm`; it takes one screen line away from the standard window for the
+labels.
+
+
+.. function:: slk_init(fmt=0)
+
+   Reserve a screen line for the soft labels and choose their layout.  *fmt*
+   selects the arrangement: ``0`` for 3-2-3 (eight labels), ``1`` for 4-4
+   (eight labels).  Where the underlying curses library supports them, ``2``
+   gives 4-4-4 (twelve labels) and ``3`` gives 4-4-4 with an index line.
+
+   Must be called before :func:`initscr` or :func:`newterm`.
+
+   .. versionadded:: next
+
+
+.. function:: slk_set(labnum, label, justify)
+
+   Set the text of soft label number *labnum*, in the range ``1`` through ``8``
+   (or ``12`` in a twelve-label layout).  *justify* controls how *label* is
+   placed within the label: ``0`` for left, ``1`` for centered, ``2`` for right.
+
+   .. versionadded:: next
+
+
+.. function:: slk_label(labnum)
+
+   Return the current text of soft label number *labnum*, justified as it was
+   set, or an empty string if it has no label.
+
+   .. versionadded:: next
+
+
+.. function:: slk_refresh()
+
+   Update the soft labels on the physical screen, like
+   :meth:`~curses.window.refresh` for a window.
+
+   .. versionadded:: next
+
+
+.. function:: slk_noutrefresh()
+
+   Update the soft labels on the virtual screen, like
+   :meth:`window.noutrefresh`.  Use it together with :func:`doupdate` to batch
+   screen updates.
+
+   .. versionadded:: next
+
+
+.. function:: slk_clear()
+
+   Remove the soft labels from the screen.
+
+   .. versionadded:: next
+
+
+.. function:: slk_restore()
+
+   Restore the soft labels to the screen after a :func:`slk_clear`.
+
+   .. versionadded:: next
+
+
+.. function:: slk_touch()
+
+   Force all the soft labels to be redrawn by the next :func:`slk_refresh` or
+   :func:`slk_noutrefresh`.
+
+   .. versionadded:: next
+
+
+.. function:: slk_attron(attr)
+              slk_attroff(attr)
+              slk_attrset(attr)
+
+   Add, remove, or set the attributes used to display the soft labels, given as
+   packed ``A_*`` attributes.
+
+   .. versionadded:: next
+
+
+.. function:: slk_attr()
+
+   Return the current attributes of the soft labels as packed ``A_*``
+   attributes.  Availability depends on the underlying curses library.
+
+   .. versionadded:: next
+
+
+.. function:: slk_attr_on(attr)
+              slk_attr_off(attr)
+
+   Turn the given attributes on or off without affecting any others.  Like the
+   ``attr_*`` window methods, these work with the
+   :ref:`WA_* attributes <curses-wa-constants>` rather than packed ``A_*``
+   attributes.
+
+   .. versionadded:: next
+
+
+.. function:: slk_attr_set(attr, pair=0)
+
+   Set the attributes and color pair of the soft labels.  *attr* is given as
+   :ref:`WA_* attributes <curses-wa-constants>` and *pair* as a color pair
+   number.
+
+   .. versionadded:: next
+
+
+.. function:: slk_color(pair)
+
+   Set the color pair of the soft labels to color pair number *pair*.
+
+   .. versionadded:: next
+
+
 Saving and restoring
 ~~~~~~~~~~~~~~~~~~~~
 
index 8407d0df3256190b165b6c0d4b41140e318654b3..e215d4ddfdf41b7870ba458462f239f027ae7266 100644 (file)
@@ -182,6 +182,18 @@ curses
   :func:`~curses.scr_set`, which dump the whole screen to a file and restore it.
   (Contributed by Serhiy Storchaka in :gh:`152260`.)
 
+* Add the soft-label-key functions to the :mod:`curses` module, which manage a
+  row of labels along the bottom line of the screen:
+  :func:`~curses.slk_init`, :func:`~curses.slk_set`, :func:`~curses.slk_label`,
+  :func:`~curses.slk_refresh`, :func:`~curses.slk_noutrefresh`,
+  :func:`~curses.slk_clear`, :func:`~curses.slk_restore`,
+  :func:`~curses.slk_touch`, the attribute functions
+  :func:`~curses.slk_attron`, :func:`~curses.slk_attroff`,
+  :func:`~curses.slk_attrset`, :func:`~curses.slk_attr`,
+  :func:`~curses.slk_attr_on`, :func:`~curses.slk_attr_off`,
+  :func:`~curses.slk_attr_set`, and :func:`~curses.slk_color`.
+  (Contributed by Serhiy Storchaka in :gh:`152263`.)
+
 * Add the :func:`curses.term_attrs` function, which returns the supported
   video attributes as :ref:`WA_* <curses-wa-constants>` values, the
   counterpart of :func:`curses.termattrs`.
index 17b31e4c7c8063cf519d2aa453a86597fbad2a4c..a18f83e23908f9c1e74419be19c3e240e6774949 100644 (file)
@@ -2843,16 +2843,12 @@ class TextboxTest(unittest.TestCase):
         self.mock_win.reset_mock()
 
 
-@unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()')
-@unittest.skipIf(not term or term == 'unknown',
-                 "$TERM=%r, newterm() may not work" % term)
-@unittest.skipIf(sys.platform == "cygwin",
-                 "cygwin's curses mostly just hangs")
-class ScreenTests(unittest.TestCase):
-    # newterm()/set_term() mutate global curses state, but each test drives its
-    # own pseudo-terminal(s) and never touches the screen shared by TestCurses,
-    # whose setUp() makes that screen current again.  So these can run in this
-    # process, without a real terminal and without a subprocess.
+class NewtermTestBase(unittest.TestCase):
+    # Shared plumbing for tests that drive newterm() over their own
+    # pseudo-terminal(s).  newterm()/set_term() mutate global curses state, but
+    # each test never touches the screen shared by TestCurses, whose setUp()
+    # makes that screen current again.  So these can run in this process,
+    # without a real terminal and without a subprocess.
 
     def setUp(self):
         # newterm() may install signal handlers; restore them afterwards.
@@ -2906,6 +2902,14 @@ class ScreenTests(unittest.TestCase):
         self.addCleanup(stop_reader)
         return slave
 
+
+@unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()')
+@unittest.skipIf(not term or term == 'unknown',
+                 f"$TERM={term!r}, newterm() may not work")
+@unittest.skipIf(sys.platform == "cygwin",
+                 "cygwin's curses mostly just hangs")
+class ScreenTests(NewtermTestBase):
+
     def test_newterm(self):
         s = self.make_pty()
         screen = curses.newterm('xterm', s, s)
@@ -2979,5 +2983,100 @@ class ScreenTests(unittest.TestCase):
         check_disallow_instantiation(self, curses.screen)
 
 
+@unittest.skipUnless(hasattr(curses, 'slk_init'), 'requires curses.slk_init()')
+@unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()')
+@unittest.skipIf(not term or term == 'unknown',
+                 f"$TERM={term!r}, newterm() may not work")
+@unittest.skipIf(sys.platform == "cygwin",
+                 "cygwin's curses mostly just hangs")
+class SLKTests(NewtermTestBase):
+    # Soft-label keys reserve the bottom screen line for a row of labels.
+    # slk_init() must run before newterm()/initscr(), so each test sets up its
+    # own screen rather than reusing the one TestCurses builds in setUp().
+
+    def make_slk_screen(self, fmt=0):
+        s = self.make_pty()
+        curses.slk_init(fmt)
+        return curses.newterm('xterm', s, s)
+
+    def test_init_reserves_a_line(self):
+        # Every layout takes the bottom line for the labels; the index-line
+        # layout (3) takes a second line for the index.  Layouts 0 and 1 are
+        # standard; 2 and 3 are ncurses extensions that other curses
+        # implementations reject (slk_init() then returns an error).
+        ncurses = hasattr(curses, 'ncurses_version')
+        for fmt, lines in [(0, 23), (1, 23), (2, 23), (3, 22)]:
+            with self.subTest(fmt=fmt):
+                try:
+                    screen = self.make_slk_screen(fmt)
+                except curses.error:
+                    if ncurses or fmt < 2:
+                        raise
+                    continue
+                self.assertEqual(screen.stdscr.getmaxyx()[0], lines)
+                curses.endwin()
+
+    def test_init_bad_format(self):
+        for fmt in (-1, 4):
+            self.assertRaises(ValueError, curses.slk_init, fmt)
+
+    def test_set_and_label(self):
+        self.make_slk_screen()
+        curses.slk_set(1, 'Help', 0)
+        curses.slk_set(2, 'Save', 1)
+        curses.slk_set(3, 'Quit', 2)
+        self.assertEqual(curses.slk_label(1), 'Help')
+        self.assertEqual(curses.slk_label(2), 'Save')
+        self.assertEqual(curses.slk_label(3), 'Quit')
+
+    def test_set_wide(self):
+        screen = self.make_slk_screen()
+        label = 'Ångström'
+        try:
+            label.encode(screen.stdscr.encoding)
+        except UnicodeEncodeError:
+            self.skipTest('the locale cannot encode %r' % label)
+        curses.slk_set(1, label, 0)
+        self.assertEqual(curses.slk_label(1), label)
+
+    def test_set_bad_justify(self):
+        self.make_slk_screen()
+        for justify in (-1, 3):
+            self.assertRaises(ValueError, curses.slk_set, 1, 'x', justify)
+
+    def test_refresh(self):
+        self.make_slk_screen()
+        curses.slk_set(1, 'Help', 0)
+        curses.slk_noutrefresh()
+        curses.slk_refresh()
+        curses.slk_clear()
+        curses.slk_restore()
+        curses.slk_touch()
+
+    def test_attributes(self):
+        self.make_slk_screen()
+        curses.slk_attron(curses.A_BOLD)
+        curses.slk_attrset(curses.A_UNDERLINE)
+        curses.slk_attroff(curses.A_BOLD)
+        if hasattr(curses, 'slk_attr'):
+            self.assertIsInstance(curses.slk_attr(), int)
+
+    def test_attr_on_off(self):
+        self.make_slk_screen()
+        curses.slk_attr_on(curses.A_BOLD)
+        curses.slk_attr_off(curses.A_BOLD)
+
+    def test_color(self):
+        # slk_attr_set() and slk_color() act on a color pair, so the color
+        # subsystem must be started first.
+        self.make_slk_screen()
+        if not curses.has_colors():
+            self.skipTest('requires colors support')
+        curses.start_color()
+        curses.slk_attr_set(curses.A_BOLD)
+        curses.slk_attr_set(curses.A_BOLD, 0)
+        curses.slk_color(0)
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2026-06-26-14-34-28.gh-issue-152263.5PHQP4.rst b/Misc/NEWS.d/next/Library/2026-06-26-14-34-28.gh-issue-152263.5PHQP4.rst
new file mode 100644 (file)
index 0000000..7367f84
--- /dev/null
@@ -0,0 +1,9 @@
+Add the soft-label-key functions to the :mod:`curses` module:
+:func:`~curses.slk_init`, :func:`~curses.slk_set`, :func:`~curses.slk_label`,
+:func:`~curses.slk_refresh`, :func:`~curses.slk_noutrefresh`,
+:func:`~curses.slk_clear`, :func:`~curses.slk_restore`,
+:func:`~curses.slk_touch`, :func:`~curses.slk_attron`,
+:func:`~curses.slk_attroff`, :func:`~curses.slk_attrset`,
+:func:`~curses.slk_attr`, :func:`~curses.slk_attr_on`,
+:func:`~curses.slk_attr_off`, :func:`~curses.slk_attr_set` and
+:func:`~curses.slk_color`.
index 07883848992a208bf56a45ab7fd8e8e587763948..cbd4521696847028e25e3ef17b62d74670c51094 100644 (file)
   tgetent tgetflag tgetnum tgetstr tgoto tputs
   vidattr vidputs
 
-  Low-priority:
-  slk_attr slk_attr_off slk_attr_on slk_attr_set slk_attroff
-  slk_attron slk_attrset slk_clear slk_color slk_init slk_label
-  slk_noutrefresh slk_refresh slk_restore slk_set slk_touch
-
   Menu extension (ncurses and probably SYSV):
   current_item free_item free_menu item_count item_description
   item_index item_init item_name item_opts item_opts_off
@@ -8088,6 +8083,304 @@ _curses_unget_wch(PyObject *module, PyObject *ch)
 #endif
 }
 
+/* ------------------------------------------------------------------------ */
+/* Soft-label keys: a row of labels along the bottom line of the screen.    */
+/* All slk_* functions are module-level and act on one global label area.   */
+/* ------------------------------------------------------------------------ */
+
+/*[clinic input]
+_curses.slk_init
+
+    fmt: int = 0
+        Label layout: 0 = 3-2-3, 1 = 4-4 (8 labels each); 2 = 4-4-4,
+        3 = 4-4-4 with an index line (12 labels each, ncurses extensions).
+    /
+
+Reserve a line for soft labels and choose their layout.
+
+Must be called before initscr() or newterm().
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_init_impl(PyObject *module, int fmt)
+/*[clinic end generated code: output=8d3da3609be1a133 input=b9fec1776f56772a]*/
+{
+    if (fmt < 0 || fmt > 3) {
+        PyErr_SetString(PyExc_ValueError,
+                        "format must be an integer from 0 to 3");
+        return NULL;
+    }
+    return curses_check_err(module, slk_init(fmt), "slk_init", NULL);
+}
+
+/*[clinic input]
+_curses.slk_set
+
+    labnum: int
+        The label number (1 to 8, or 1 to 12 in a 12-label layout).
+    label: unicode
+        The text to display.
+    justify: int = 0
+        0 = left, 1 = center, 2 = right.
+    /
+
+Set the text of a soft label.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_set_impl(PyObject *module, int labnum, PyObject *label,
+                     int justify)
+/*[clinic end generated code: output=fc0f6b7af5acf32d input=1ce82b6cf23504d1]*/
+{
+    PyCursesStatefulInitialised(module);
+    if (justify < 0 || justify > 2) {
+        PyErr_SetString(PyExc_ValueError,
+                        "justification must be an integer from 0 to 2");
+        return NULL;
+    }
+    int rtn;
+#ifdef HAVE_NCURSESW
+    wchar_t *wstr = PyUnicode_AsWideCharString(label, NULL);
+    if (wstr == NULL) {
+        return NULL;
+    }
+    rtn = slk_wset(labnum, wstr, justify);
+    PyMem_Free(wstr);
+#else
+    PyObject *bytes = PyUnicode_EncodeLocale(label, NULL);
+    if (bytes == NULL) {
+        return NULL;
+    }
+    rtn = slk_set(labnum, PyBytes_AS_STRING(bytes), justify);
+    Py_DECREF(bytes);
+#endif
+    return curses_check_err(module, rtn, "slk_set", NULL);
+}
+
+/*[clinic input]
+_curses.slk_label
+
+    labnum: int
+        The label number.
+    /
+
+Return the current text of a soft label.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_label_impl(PyObject *module, int labnum)
+/*[clinic end generated code: output=4f4945ceaa0db758 input=ad9c26a136555ea0]*/
+{
+    PyCursesStatefulInitialised(module);
+    char *label = slk_label(labnum);
+    if (label == NULL) {
+        return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
+    }
+    return PyUnicode_DecodeLocale(label, NULL);
+}
+
+/*[clinic input]
+_curses.slk_refresh
+
+Update the soft labels on the screen.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_refresh_impl(PyObject *module)
+/*[clinic end generated code: output=93183b9300e29cfe input=c668ee5b14ecb802]*/
+NoArgNoReturnFunctionBody(slk_refresh)
+
+/*[clinic input]
+_curses.slk_noutrefresh
+
+Update the soft labels on the virtual screen only.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_noutrefresh_impl(PyObject *module)
+/*[clinic end generated code: output=043d1d0021331e48 input=cabc0f5e37aac369]*/
+NoArgNoReturnFunctionBody(slk_noutrefresh)
+
+/*[clinic input]
+_curses.slk_clear
+
+Erase the soft labels from the screen.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_clear_impl(PyObject *module)
+/*[clinic end generated code: output=acf24fa9b130c8c6 input=38644dc752e4372b]*/
+NoArgNoReturnFunctionBody(slk_clear)
+
+/*[clinic input]
+_curses.slk_restore
+
+Restore the soft labels after a preceding slk_clear().
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_restore_impl(PyObject *module)
+/*[clinic end generated code: output=fe9a518a013a00de input=97346ac473b0f9d7]*/
+NoArgNoReturnFunctionBody(slk_restore)
+
+/*[clinic input]
+_curses.slk_touch
+
+Force the soft labels to be redrawn by the next slk_refresh().
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_touch_impl(PyObject *module)
+/*[clinic end generated code: output=6135e95a69687969 input=ff45098b9d8c9417]*/
+NoArgNoReturnFunctionBody(slk_touch)
+
+/*[clinic input]
+_curses.slk_attron
+
+    attr: long
+    /
+
+Add the given chtype attributes to the soft labels.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_attron_impl(PyObject *module, long attr)
+/*[clinic end generated code: output=01aa29848a58ab50 input=fa198a604e3eec04]*/
+{
+    PyCursesStatefulInitialised(module);
+    return curses_check_err(module, slk_attron((chtype)attr),
+                            "slk_attron", NULL);
+}
+
+/*[clinic input]
+_curses.slk_attroff
+
+    attr: long
+    /
+
+Remove the given chtype attributes from the soft labels.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_attroff_impl(PyObject *module, long attr)
+/*[clinic end generated code: output=7b172cc37a17811f input=21dab55d43d30b8f]*/
+{
+    PyCursesStatefulInitialised(module);
+    return curses_check_err(module, slk_attroff((chtype)attr),
+                            "slk_attroff", NULL);
+}
+
+/*[clinic input]
+_curses.slk_attrset
+
+    attr: long
+    /
+
+Set the chtype attributes of the soft labels.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_attrset_impl(PyObject *module, long attr)
+/*[clinic end generated code: output=1139e2b0f757edfd input=d5c798956a5f046a]*/
+{
+    PyCursesStatefulInitialised(module);
+    return curses_check_err(module, slk_attrset((chtype)attr),
+                            "slk_attrset", NULL);
+}
+
+#ifdef NCURSES_EXT_FUNCS
+/*[clinic input]
+_curses.slk_attr
+
+Return the current chtype attributes of the soft labels.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_attr_impl(PyObject *module)
+/*[clinic end generated code: output=6d47752f82bdc29f input=be38805fdec52149]*/
+{
+    PyCursesStatefulInitialised(module);
+    return PyLong_FromUnsignedLong((unsigned long)slk_attr());
+}
+#endif
+
+/*[clinic input]
+_curses.slk_attr_on
+
+    attr: attr
+    /
+
+Turn on attributes of the soft labels without affecting others.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_attr_on_impl(PyObject *module, attr_t attr)
+/*[clinic end generated code: output=32419d75e53e01c1 input=1087c3c4ecf21080]*/
+{
+    PyCursesStatefulInitialised(module);
+    return curses_check_err(module, slk_attr_on(attr, NULL),
+                            "slk_attr_on", NULL);
+}
+
+/*[clinic input]
+_curses.slk_attr_off
+
+    attr: attr
+    /
+
+Turn off attributes of the soft labels without affecting others.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_attr_off_impl(PyObject *module, attr_t attr)
+/*[clinic end generated code: output=28c6235ac6bc923c input=02b472ca7c772a66]*/
+{
+    PyCursesStatefulInitialised(module);
+    return curses_check_err(module, slk_attr_off(attr, NULL),
+                            "slk_attr_off", NULL);
+}
+
+/*[clinic input]
+_curses.slk_attr_set
+
+    attr: attr
+    pair: pair = 0
+    /
+
+Set the attributes and color pair of the soft labels.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_attr_set_impl(PyObject *module, attr_t attr, int pair)
+/*[clinic end generated code: output=b93f23465e232597 input=4502a201917e4bf4]*/
+{
+    PyCursesStatefulInitialised(module);
+    int rtn;
+#if _NCURSES_EXTENDED_COLOR_FUNCS
+    rtn = slk_attr_set(attr, 0, &pair);
+#else
+    rtn = slk_attr_set(attr, (short)pair, NULL);
+#endif
+    return curses_check_err(module, rtn, "slk_attr_set", NULL);
+}
+
+/*[clinic input]
+_curses.slk_color
+
+    pair: pair
+    /
+
+Set the color pair of the soft labels.
+[clinic start generated code]*/
+
+static PyObject *
+_curses_slk_color_impl(PyObject *module, int pair)
+/*[clinic end generated code: output=ffe4de805f9c65f5 input=b1e691a9cc6177ee]*/
+{
+    PyCursesStatefulInitialised(module);
+    return curses_check_err(module, slk_color((short)pair), "slk_color", NULL);
+}
+
 #ifdef HAVE_CURSES_USE_ENV
 /*[clinic input]
 _curses.use_env
@@ -8330,6 +8623,22 @@ static PyMethodDef cursesmodule_methods[] = {
     _CURSES_SET_TERM_METHODDEF
     _CURSES_SETSYX_METHODDEF
     _CURSES_SETUPTERM_METHODDEF
+    _CURSES_SLK_INIT_METHODDEF
+    _CURSES_SLK_SET_METHODDEF
+    _CURSES_SLK_LABEL_METHODDEF
+    _CURSES_SLK_REFRESH_METHODDEF
+    _CURSES_SLK_NOUTREFRESH_METHODDEF
+    _CURSES_SLK_CLEAR_METHODDEF
+    _CURSES_SLK_RESTORE_METHODDEF
+    _CURSES_SLK_TOUCH_METHODDEF
+    _CURSES_SLK_ATTRON_METHODDEF
+    _CURSES_SLK_ATTROFF_METHODDEF
+    _CURSES_SLK_ATTRSET_METHODDEF
+    _CURSES_SLK_ATTR_METHODDEF
+    _CURSES_SLK_ATTR_ON_METHODDEF
+    _CURSES_SLK_ATTR_OFF_METHODDEF
+    _CURSES_SLK_ATTR_SET_METHODDEF
+    _CURSES_SLK_COLOR_METHODDEF
     _CURSES_START_COLOR_METHODDEF
     _CURSES_TERMATTRS_METHODDEF
     _CURSES_TERM_ATTRS_METHODDEF
index 1bd1e9b2554bd897c5ba6a75a0e7c3c2a1715377..78619842f97fd778be2023949a56614fac4bef0e 100644 (file)
@@ -5433,6 +5433,447 @@ PyDoc_STRVAR(_curses_unget_wch__doc__,
 #define _CURSES_UNGET_WCH_METHODDEF    \
     {"unget_wch", (PyCFunction)_curses_unget_wch, METH_O, _curses_unget_wch__doc__},
 
+PyDoc_STRVAR(_curses_slk_init__doc__,
+"slk_init($module, fmt=0, /)\n"
+"--\n"
+"\n"
+"Reserve a line for soft labels and choose their layout.\n"
+"\n"
+"  fmt\n"
+"    Label layout: 0 = 3-2-3, 1 = 4-4 (8 labels each); 2 = 4-4-4,\n"
+"    3 = 4-4-4 with an index line (12 labels each, ncurses extensions).\n"
+"\n"
+"Must be called before initscr() or newterm().");
+
+#define _CURSES_SLK_INIT_METHODDEF    \
+    {"slk_init", _PyCFunction_CAST(_curses_slk_init), METH_FASTCALL, _curses_slk_init__doc__},
+
+static PyObject *
+_curses_slk_init_impl(PyObject *module, int fmt);
+
+static PyObject *
+_curses_slk_init(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    int fmt = 0;
+
+    if (!_PyArg_CheckPositional("slk_init", nargs, 0, 1)) {
+        goto exit;
+    }
+    if (nargs < 1) {
+        goto skip_optional;
+    }
+    fmt = PyLong_AsInt(args[0]);
+    if (fmt == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional:
+    return_value = _curses_slk_init_impl(module, fmt);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_set__doc__,
+"slk_set($module, labnum, label, justify=0, /)\n"
+"--\n"
+"\n"
+"Set the text of a soft label.\n"
+"\n"
+"  labnum\n"
+"    The label number (1 to 8, or 1 to 12 in a 12-label layout).\n"
+"  label\n"
+"    The text to display.\n"
+"  justify\n"
+"    0 = left, 1 = center, 2 = right.");
+
+#define _CURSES_SLK_SET_METHODDEF    \
+    {"slk_set", _PyCFunction_CAST(_curses_slk_set), METH_FASTCALL, _curses_slk_set__doc__},
+
+static PyObject *
+_curses_slk_set_impl(PyObject *module, int labnum, PyObject *label,
+                     int justify);
+
+static PyObject *
+_curses_slk_set(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    int labnum;
+    PyObject *label;
+    int justify = 0;
+
+    if (!_PyArg_CheckPositional("slk_set", nargs, 2, 3)) {
+        goto exit;
+    }
+    labnum = PyLong_AsInt(args[0]);
+    if (labnum == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    if (!PyUnicode_Check(args[1])) {
+        _PyArg_BadArgument("slk_set", "argument 2", "str", args[1]);
+        goto exit;
+    }
+    label = args[1];
+    if (nargs < 3) {
+        goto skip_optional;
+    }
+    justify = PyLong_AsInt(args[2]);
+    if (justify == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional:
+    return_value = _curses_slk_set_impl(module, labnum, label, justify);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_label__doc__,
+"slk_label($module, labnum, /)\n"
+"--\n"
+"\n"
+"Return the current text of a soft label.\n"
+"\n"
+"  labnum\n"
+"    The label number.");
+
+#define _CURSES_SLK_LABEL_METHODDEF    \
+    {"slk_label", (PyCFunction)_curses_slk_label, METH_O, _curses_slk_label__doc__},
+
+static PyObject *
+_curses_slk_label_impl(PyObject *module, int labnum);
+
+static PyObject *
+_curses_slk_label(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    int labnum;
+
+    labnum = PyLong_AsInt(arg);
+    if (labnum == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    return_value = _curses_slk_label_impl(module, labnum);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_refresh__doc__,
+"slk_refresh($module, /)\n"
+"--\n"
+"\n"
+"Update the soft labels on the screen.");
+
+#define _CURSES_SLK_REFRESH_METHODDEF    \
+    {"slk_refresh", (PyCFunction)_curses_slk_refresh, METH_NOARGS, _curses_slk_refresh__doc__},
+
+static PyObject *
+_curses_slk_refresh_impl(PyObject *module);
+
+static PyObject *
+_curses_slk_refresh(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _curses_slk_refresh_impl(module);
+}
+
+PyDoc_STRVAR(_curses_slk_noutrefresh__doc__,
+"slk_noutrefresh($module, /)\n"
+"--\n"
+"\n"
+"Update the soft labels on the virtual screen only.");
+
+#define _CURSES_SLK_NOUTREFRESH_METHODDEF    \
+    {"slk_noutrefresh", (PyCFunction)_curses_slk_noutrefresh, METH_NOARGS, _curses_slk_noutrefresh__doc__},
+
+static PyObject *
+_curses_slk_noutrefresh_impl(PyObject *module);
+
+static PyObject *
+_curses_slk_noutrefresh(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _curses_slk_noutrefresh_impl(module);
+}
+
+PyDoc_STRVAR(_curses_slk_clear__doc__,
+"slk_clear($module, /)\n"
+"--\n"
+"\n"
+"Erase the soft labels from the screen.");
+
+#define _CURSES_SLK_CLEAR_METHODDEF    \
+    {"slk_clear", (PyCFunction)_curses_slk_clear, METH_NOARGS, _curses_slk_clear__doc__},
+
+static PyObject *
+_curses_slk_clear_impl(PyObject *module);
+
+static PyObject *
+_curses_slk_clear(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _curses_slk_clear_impl(module);
+}
+
+PyDoc_STRVAR(_curses_slk_restore__doc__,
+"slk_restore($module, /)\n"
+"--\n"
+"\n"
+"Restore the soft labels after a preceding slk_clear().");
+
+#define _CURSES_SLK_RESTORE_METHODDEF    \
+    {"slk_restore", (PyCFunction)_curses_slk_restore, METH_NOARGS, _curses_slk_restore__doc__},
+
+static PyObject *
+_curses_slk_restore_impl(PyObject *module);
+
+static PyObject *
+_curses_slk_restore(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _curses_slk_restore_impl(module);
+}
+
+PyDoc_STRVAR(_curses_slk_touch__doc__,
+"slk_touch($module, /)\n"
+"--\n"
+"\n"
+"Force the soft labels to be redrawn by the next slk_refresh().");
+
+#define _CURSES_SLK_TOUCH_METHODDEF    \
+    {"slk_touch", (PyCFunction)_curses_slk_touch, METH_NOARGS, _curses_slk_touch__doc__},
+
+static PyObject *
+_curses_slk_touch_impl(PyObject *module);
+
+static PyObject *
+_curses_slk_touch(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _curses_slk_touch_impl(module);
+}
+
+PyDoc_STRVAR(_curses_slk_attron__doc__,
+"slk_attron($module, attr, /)\n"
+"--\n"
+"\n"
+"Add the given chtype attributes to the soft labels.");
+
+#define _CURSES_SLK_ATTRON_METHODDEF    \
+    {"slk_attron", (PyCFunction)_curses_slk_attron, METH_O, _curses_slk_attron__doc__},
+
+static PyObject *
+_curses_slk_attron_impl(PyObject *module, long attr);
+
+static PyObject *
+_curses_slk_attron(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    long attr;
+
+    attr = PyLong_AsLong(arg);
+    if (attr == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    return_value = _curses_slk_attron_impl(module, attr);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_attroff__doc__,
+"slk_attroff($module, attr, /)\n"
+"--\n"
+"\n"
+"Remove the given chtype attributes from the soft labels.");
+
+#define _CURSES_SLK_ATTROFF_METHODDEF    \
+    {"slk_attroff", (PyCFunction)_curses_slk_attroff, METH_O, _curses_slk_attroff__doc__},
+
+static PyObject *
+_curses_slk_attroff_impl(PyObject *module, long attr);
+
+static PyObject *
+_curses_slk_attroff(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    long attr;
+
+    attr = PyLong_AsLong(arg);
+    if (attr == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    return_value = _curses_slk_attroff_impl(module, attr);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_attrset__doc__,
+"slk_attrset($module, attr, /)\n"
+"--\n"
+"\n"
+"Set the chtype attributes of the soft labels.");
+
+#define _CURSES_SLK_ATTRSET_METHODDEF    \
+    {"slk_attrset", (PyCFunction)_curses_slk_attrset, METH_O, _curses_slk_attrset__doc__},
+
+static PyObject *
+_curses_slk_attrset_impl(PyObject *module, long attr);
+
+static PyObject *
+_curses_slk_attrset(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    long attr;
+
+    attr = PyLong_AsLong(arg);
+    if (attr == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    return_value = _curses_slk_attrset_impl(module, attr);
+
+exit:
+    return return_value;
+}
+
+#if defined(NCURSES_EXT_FUNCS)
+
+PyDoc_STRVAR(_curses_slk_attr__doc__,
+"slk_attr($module, /)\n"
+"--\n"
+"\n"
+"Return the current chtype attributes of the soft labels.");
+
+#define _CURSES_SLK_ATTR_METHODDEF    \
+    {"slk_attr", (PyCFunction)_curses_slk_attr, METH_NOARGS, _curses_slk_attr__doc__},
+
+static PyObject *
+_curses_slk_attr_impl(PyObject *module);
+
+static PyObject *
+_curses_slk_attr(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+    return _curses_slk_attr_impl(module);
+}
+
+#endif /* defined(NCURSES_EXT_FUNCS) */
+
+PyDoc_STRVAR(_curses_slk_attr_on__doc__,
+"slk_attr_on($module, attr, /)\n"
+"--\n"
+"\n"
+"Turn on attributes of the soft labels without affecting others.");
+
+#define _CURSES_SLK_ATTR_ON_METHODDEF    \
+    {"slk_attr_on", (PyCFunction)_curses_slk_attr_on, METH_O, _curses_slk_attr_on__doc__},
+
+static PyObject *
+_curses_slk_attr_on_impl(PyObject *module, attr_t attr);
+
+static PyObject *
+_curses_slk_attr_on(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    attr_t attr;
+
+    if (!attr_converter(arg, &attr)) {
+        goto exit;
+    }
+    return_value = _curses_slk_attr_on_impl(module, attr);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_attr_off__doc__,
+"slk_attr_off($module, attr, /)\n"
+"--\n"
+"\n"
+"Turn off attributes of the soft labels without affecting others.");
+
+#define _CURSES_SLK_ATTR_OFF_METHODDEF    \
+    {"slk_attr_off", (PyCFunction)_curses_slk_attr_off, METH_O, _curses_slk_attr_off__doc__},
+
+static PyObject *
+_curses_slk_attr_off_impl(PyObject *module, attr_t attr);
+
+static PyObject *
+_curses_slk_attr_off(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    attr_t attr;
+
+    if (!attr_converter(arg, &attr)) {
+        goto exit;
+    }
+    return_value = _curses_slk_attr_off_impl(module, attr);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_attr_set__doc__,
+"slk_attr_set($module, attr, pair=0, /)\n"
+"--\n"
+"\n"
+"Set the attributes and color pair of the soft labels.");
+
+#define _CURSES_SLK_ATTR_SET_METHODDEF    \
+    {"slk_attr_set", _PyCFunction_CAST(_curses_slk_attr_set), METH_FASTCALL, _curses_slk_attr_set__doc__},
+
+static PyObject *
+_curses_slk_attr_set_impl(PyObject *module, attr_t attr, int pair);
+
+static PyObject *
+_curses_slk_attr_set(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    attr_t attr;
+    int pair = 0;
+
+    if (!_PyArg_CheckPositional("slk_attr_set", nargs, 1, 2)) {
+        goto exit;
+    }
+    if (!attr_converter(args[0], &attr)) {
+        goto exit;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    if (!pair_converter(args[1], &pair)) {
+        goto exit;
+    }
+skip_optional:
+    return_value = _curses_slk_attr_set_impl(module, attr, pair);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_curses_slk_color__doc__,
+"slk_color($module, pair, /)\n"
+"--\n"
+"\n"
+"Set the color pair of the soft labels.");
+
+#define _CURSES_SLK_COLOR_METHODDEF    \
+    {"slk_color", (PyCFunction)_curses_slk_color, METH_O, _curses_slk_color__doc__},
+
+static PyObject *
+_curses_slk_color_impl(PyObject *module, int pair);
+
+static PyObject *
+_curses_slk_color(PyObject *module, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    int pair;
+
+    if (!pair_converter(arg, &pair)) {
+        goto exit;
+    }
+    return_value = _curses_slk_color_impl(module, pair);
+
+exit:
+    return return_value;
+}
+
 #if defined(HAVE_CURSES_USE_ENV)
 
 PyDoc_STRVAR(_curses_use_env__doc__,
@@ -5718,6 +6159,10 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored
     #define _CURSES_TYPEAHEAD_METHODDEF
 #endif /* !defined(_CURSES_TYPEAHEAD_METHODDEF) */
 
+#ifndef _CURSES_SLK_ATTR_METHODDEF
+    #define _CURSES_SLK_ATTR_METHODDEF
+#endif /* !defined(_CURSES_SLK_ATTR_METHODDEF) */
+
 #ifndef _CURSES_USE_ENV_METHODDEF
     #define _CURSES_USE_ENV_METHODDEF
 #endif /* !defined(_CURSES_USE_ENV_METHODDEF) */
@@ -5729,4 +6174,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=0cf212f804ab3d85 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=55829d2ef2b559a0 input=a9049054013a1b77]*/