diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index f582336fae17344..7ce093492f035a9 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -3166,6 +3166,44 @@ def test_use_prescr_screen(self): # The current screen is unchanged. screen.stdscr.refresh() + @unittest.skipUnless(hasattr(curses, 'new_prescr'), + 'requires curses.new_prescr()') + def test_new_prescr_returns_existing_screen(self): + pre1 = curses.new_prescr() + pre2 = curses.new_prescr() + self.assertIs(pre1, pre2) + + @unittest.skipUnless(hasattr(curses, 'new_prescr'), + 'requires curses.new_prescr()') + def test_newterm_after_new_prescr_keeps_screen_alive(self): + # newterm() adopts the SCREEN created by new_prescr(). Dropping the + # pre-screen wrapper must not delete the live screen. + s = self.make_pty() + pre = curses.new_prescr() + screen = curses.newterm('xterm', s, s) + del pre + gc_collect() + screen.stdscr.addstr(0, 0, 'x') + screen.stdscr.refresh() + + @unittest.skipUnless(hasattr(curses, 'new_prescr'), + 'requires curses.new_prescr()') + def test_initscr_after_new_prescr_keeps_screen_alive(self): + # initscr() adopts the SCREEN created by new_prescr(). Dropping the + # pre-screen wrapper must not delete the live screen. + s = self.make_pty() + saved = os.dup(1) + self.addCleanup(os.close, saved) + self.addCleanup(os.dup2, saved, 1) + os.dup2(s, 1) + + pre = curses.new_prescr() + stdscr = curses.initscr() + del pre + gc_collect() + stdscr.addstr(0, 0, 'x') + stdscr.refresh() + def test_initscr_after_newterm_keeps_screen_alive(self): # initscr() called while a newterm() screen is current returns that # screen's own standard window, so the window keeps the screen alive. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 006e27d55d8925d..28a63f61debf3c4 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -159,6 +159,8 @@ typedef struct { PyTypeObject *complexstr_type; // _curses.complexstr PyObject *topscreen; // owned ref to the current screen object, // or NULL for the initscr() screen + PyObject *prescreen; // owned ref to the pending new_prescr() screen, + // or NULL if there is no pending pre-screen } cursesmodule_state; static inline cursesmodule_state * @@ -6619,13 +6621,21 @@ _curses_initscr_impl(PyObject *module) return NULL; } + cursesmodule_state *state = get_cursesmodule_state(module); + if (state->prescreen != NULL) { + PyCursesScreenObject *prescreen = + _PyCursesScreenObject_CAST(state->prescreen); + assert(prescreen->screen != NULL); + prescreen->screen = NULL; + Py_CLEAR(state->prescreen); + } + curses_initscr_called = curses_setupterm_called = TRUE; if (curses_init_dict(module) < 0) { return NULL; } - cursesmodule_state *state = get_cursesmodule_state(module); PyObject *winobj = PyCursesWindow_New(state, win, NULL, NULL, NULL); if (winobj == NULL) { return NULL; @@ -6801,6 +6811,13 @@ _curses_newterm_impl(PyObject *module, const char *type, PyObject *fd, cursesmodule_state *state = get_cursesmodule_state(module); /* The screen object owns the SCREEN and the streams; deleting it (when it is no longer referenced) calls delscreen() and closes the streams. */ + if (state->prescreen != NULL) { + PyCursesScreenObject *prescreen = + _PyCursesScreenObject_CAST(state->prescreen); + assert(prescreen->screen == screen); + prescreen->screen = NULL; + Py_CLEAR(state->prescreen); + } PyObject *screenobj = PyCursesScreen_New(state, screen, outfp, infp, NULL); if (screenobj == NULL) { delscreen(screen); @@ -6892,13 +6909,25 @@ static PyObject * _curses_new_prescr_impl(PyObject *module) /*[clinic end generated code: output=e7de5031da7511e2 input=1a3a89d630b641c3]*/ { + cursesmodule_state *state = get_cursesmodule_state(module); + if (state->prescreen != NULL) { + return Py_NewRef(state->prescreen); + } + SCREEN *screen = new_prescr(); if (screen == NULL) { curses_set_null_error(module, "new_prescr", NULL); return NULL; } - cursesmodule_state *state = get_cursesmodule_state(module); - return PyCursesScreen_New(state, screen, NULL, NULL, NULL); + + PyObject *screenobj = PyCursesScreen_New(state, screen, NULL, NULL, NULL); + if (screenobj == NULL) { + delscreen(screen); + return NULL; + } + + state->prescreen = Py_NewRef(screenobj); + return screenobj; } #endif /* HAVE_CURSES_NEW_PRESCR */ @@ -8894,6 +8923,7 @@ cursesmodule_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->complexchar_type); Py_VISIT(state->complexstr_type); Py_VISIT(state->topscreen); + Py_VISIT(state->prescreen); return 0; } @@ -8907,6 +8937,7 @@ cursesmodule_clear(PyObject *mod) Py_CLEAR(state->complexchar_type); Py_CLEAR(state->complexstr_type); Py_CLEAR(state->topscreen); + Py_CLEAR(state->prescreen); return 0; }