Skip to content

Commit 1106e38

Browse files
committed
gh-155875: Fix new_prescr screen lifetime
1 parent 7a845ce commit 1106e38

2 files changed

Lines changed: 52 additions & 2 deletions

File tree

Lib/test/test_curses.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3166,6 +3166,31 @@ def test_use_prescr_screen(self):
31663166
# The current screen is unchanged.
31673167
screen.stdscr.refresh()
31683168

3169+
@unittest.skipUnless(hasattr(curses, 'new_prescr'),
3170+
'requires curses.new_prescr()')
3171+
def test_newterm_after_new_prescr_keeps_screen_alive(self):
3172+
# newterm() adopts the SCREEN created by new_prescr(). Dropping the
3173+
# pre-screen wrapper must not delete the live screen.
3174+
s = self.make_pty()
3175+
pre = curses.new_prescr()
3176+
screen = curses.newterm('xterm', s, s)
3177+
del pre
3178+
gc_collect()
3179+
screen.stdscr.addstr(0, 0, 'x')
3180+
screen.stdscr.refresh()
3181+
3182+
@unittest.skipUnless(hasattr(curses, 'new_prescr'),
3183+
'requires curses.new_prescr()')
3184+
def test_initscr_after_new_prescr_keeps_screen_alive(self):
3185+
# initscr() adopts the SCREEN created by new_prescr(). Dropping the
3186+
# pre-screen wrapper must not delete the live screen.
3187+
pre = curses.new_prescr()
3188+
stdscr = curses.initscr()
3189+
del pre
3190+
gc_collect()
3191+
stdscr.addstr(0, 0, 'x')
3192+
stdscr.refresh()
3193+
31693194
def test_initscr_after_newterm_keeps_screen_alive(self):
31703195
# initscr() called while a newterm() screen is current returns that
31713196
# screen's own standard window, so the window keeps the screen alive.

Modules/_cursesmodule.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ typedef struct {
159159
PyTypeObject *complexstr_type; // _curses.complexstr
160160
PyObject *topscreen; // owned ref to the current screen object,
161161
// or NULL for the initscr() screen
162+
PyObject *prescreen; // owned ref to the pending new_prescr() screen,
163+
// or NULL if there is no pending pre-screen
162164
} cursesmodule_state;
163165

164166
static inline cursesmodule_state *
@@ -6619,13 +6621,21 @@ _curses_initscr_impl(PyObject *module)
66196621
return NULL;
66206622
}
66216623

6624+
cursesmodule_state *state = get_cursesmodule_state(module);
6625+
if (state->prescreen != NULL) {
6626+
PyCursesScreenObject *prescreen =
6627+
_PyCursesScreenObject_CAST(state->prescreen);
6628+
assert(prescreen->screen != NULL);
6629+
prescreen->screen = NULL;
6630+
Py_CLEAR(state->prescreen);
6631+
}
6632+
66226633
curses_initscr_called = curses_setupterm_called = TRUE;
66236634

66246635
if (curses_init_dict(module) < 0) {
66256636
return NULL;
66266637
}
66276638

6628-
cursesmodule_state *state = get_cursesmodule_state(module);
66296639
PyObject *winobj = PyCursesWindow_New(state, win, NULL, NULL, NULL);
66306640
if (winobj == NULL) {
66316641
return NULL;
@@ -6801,6 +6811,13 @@ _curses_newterm_impl(PyObject *module, const char *type, PyObject *fd,
68016811
cursesmodule_state *state = get_cursesmodule_state(module);
68026812
/* The screen object owns the SCREEN and the streams; deleting it (when it
68036813
is no longer referenced) calls delscreen() and closes the streams. */
6814+
if (state->prescreen != NULL) {
6815+
PyCursesScreenObject *prescreen =
6816+
_PyCursesScreenObject_CAST(state->prescreen);
6817+
assert(prescreen->screen == screen);
6818+
prescreen->screen = NULL;
6819+
Py_CLEAR(state->prescreen);
6820+
}
68046821
PyObject *screenobj = PyCursesScreen_New(state, screen, outfp, infp, NULL);
68056822
if (screenobj == NULL) {
68066823
delscreen(screen);
@@ -6898,7 +6915,13 @@ _curses_new_prescr_impl(PyObject *module)
68986915
return NULL;
68996916
}
69006917
cursesmodule_state *state = get_cursesmodule_state(module);
6901-
return PyCursesScreen_New(state, screen, NULL, NULL, NULL);
6918+
PyObject *screenobj = PyCursesScreen_New(state, screen, NULL, NULL, NULL);
6919+
if (screenobj == NULL) {
6920+
delscreen(screen);
6921+
return NULL;
6922+
}
6923+
Py_XSETREF(state->prescreen, Py_NewRef(screenobj));
6924+
return screenobj;
69026925
}
69036926
#endif /* HAVE_CURSES_NEW_PRESCR */
69046927

@@ -8894,6 +8917,7 @@ cursesmodule_traverse(PyObject *mod, visitproc visit, void *arg)
88948917
Py_VISIT(state->complexchar_type);
88958918
Py_VISIT(state->complexstr_type);
88968919
Py_VISIT(state->topscreen);
8920+
Py_VISIT(state->prescreen);
88978921
return 0;
88988922
}
88998923

@@ -8907,6 +8931,7 @@ cursesmodule_clear(PyObject *mod)
89078931
Py_CLEAR(state->complexchar_type);
89088932
Py_CLEAR(state->complexstr_type);
89098933
Py_CLEAR(state->topscreen);
8934+
Py_CLEAR(state->prescreen);
89108935
return 0;
89118936
}
89128937

0 commit comments

Comments
 (0)