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
38 changes: 38 additions & 0 deletions Lib/test/test_curses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
37 changes: 34 additions & 3 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */

Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand Down
Loading