Skip to content

Commit ae224bb

Browse files
authored
bpo-41818: Add termios.tcgetwinsize(), termios.tcsetwinsize(). (GH-23686)
* Add termios.tcgetwinsize(), termios.tcsetwinsize(). Update docs. * Add TIOCGSIZE support to termios.tcgetwinsize() * Add TIOCSSIZE support to termios.tcsetwinsize() Authored-by: Soumendra Ganguly <soumendraganguly@gmail.com> * termios.tcgetwinsize() and termios.tcsetwinsize() should return/accept two-item tuples instead of lists. * Refactor tcsetwinsize to share common code and accept any two item sequence, with overflow checking. Co-authored-by: Gregory P. Smith <greg@krypto.org> [Google]
1 parent 969ae7f commit ae224bb

File tree

4 files changed

+240
-1
lines changed

4 files changed

+240
-1
lines changed

Doc/library/termios.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,22 @@ The module defines the following functions:
7474
output, :const:`TCIOFF` to suspend input, or :const:`TCION` to restart input.
7575

7676

77+
.. function:: tcgetwinsize(fd)
78+
79+
Return a tuple ``(ws_row, ws_col)`` containing the tty window size for file
80+
descriptor *fd*. Requires :const:`termios.TIOCGWINSZ` or
81+
:const:`termios.TIOCGSIZE`.
82+
83+
84+
.. function:: tcsetwinsize(fd, winsize)
85+
86+
Set the tty window size for file descriptor *fd* from *winsize*, which is
87+
a two-item tuple ``(ws_row, ws_col)`` like the one returned by
88+
:func:`tcgetwinsize`. Requires at least one of the pairs
89+
(:const:`termios.TIOCGWINSZ`, :const:`termios.TIOCSWINSZ`);
90+
(:const:`termios.TIOCGSIZE`, :const:`termios.TIOCSSIZE`) to be defined.
91+
92+
7793
.. seealso::
7894

7995
Module :mod:`tty`
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Soumendra Ganguly: add termios.tcgetwinsize(), termios.tcsetwinsize().

Modules/clinic/termios.c.h

Lines changed: 65 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/termios.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,156 @@ termios_tcflow_impl(PyObject *module, int fd, int action)
315315
Py_RETURN_NONE;
316316
}
317317

318+
/*[clinic input]
319+
termios.tcgetwinsize
320+
321+
fd: fildes
322+
/
323+
324+
Get the tty winsize for file descriptor fd.
325+
326+
Returns a tuple (ws_row, ws_col).
327+
[clinic start generated code]*/
328+
329+
static PyObject *
330+
termios_tcgetwinsize_impl(PyObject *module, int fd)
331+
/*[clinic end generated code: output=31825977d5325fb6 input=5706c379d7fd984d]*/
332+
{
333+
#if defined(TIOCGWINSZ)
334+
termiosmodulestate *state = PyModule_GetState(module);
335+
struct winsize w;
336+
if (ioctl(fd, TIOCGWINSZ, &w) == -1) {
337+
return PyErr_SetFromErrno(state->TermiosError);
338+
}
339+
340+
PyObject *v;
341+
if (!(v = PyTuple_New(2))) {
342+
return NULL;
343+
}
344+
345+
PyTuple_SetItem(v, 0, PyLong_FromLong((long)w.ws_row));
346+
PyTuple_SetItem(v, 1, PyLong_FromLong((long)w.ws_col));
347+
if (PyErr_Occurred()) {
348+
Py_DECREF(v);
349+
return NULL;
350+
}
351+
return v;
352+
#elif defined(TIOCGSIZE)
353+
termiosmodulestate *state = PyModule_GetState(module);
354+
struct ttysize s;
355+
if (ioctl(fd, TIOCGSIZE, &s) == -1) {
356+
return PyErr_SetFromErrno(state->TermiosError);
357+
}
358+
359+
PyObject *v;
360+
if (!(v = PyTuple_New(2))) {
361+
return NULL;
362+
}
363+
364+
PyTuple_SetItem(v, 0, PyLong_FromLong((long)s.ts_lines));
365+
PyTuple_SetItem(v, 1, PyLong_FromLong((long)s.ts_cols));
366+
if (PyErr_Occurred()) {
367+
Py_DECREF(v);
368+
return NULL;
369+
}
370+
return v;
371+
#else
372+
PyErr_SetString(PyExc_NotImplementedError,
373+
"requires termios.TIOCGWINSZ and/or termios.TIOCGSIZE");
374+
return NULL;
375+
#endif /* defined(TIOCGWINSZ) */
376+
}
377+
378+
/*[clinic input]
379+
termios.tcsetwinsize
380+
381+
fd: fildes
382+
winsize as winsz: object
383+
/
384+
385+
Set the tty winsize for file descriptor fd.
386+
387+
The winsize to be set is taken from the winsize argument, which
388+
is a two-item tuple (ws_row, ws_col) like the one returned by tcgetwinsize().
389+
[clinic start generated code]*/
390+
391+
static PyObject *
392+
termios_tcsetwinsize_impl(PyObject *module, int fd, PyObject *winsz)
393+
/*[clinic end generated code: output=2ac3c9bb6eda83e1 input=4a06424465b24aee]*/
394+
{
395+
if (!PySequence_Check(winsz) || PySequence_Size(winsz) != 2) {
396+
PyErr_SetString(PyExc_TypeError,
397+
"tcsetwinsize, arg 2: must be a two-item sequence");
398+
return NULL;
399+
}
400+
401+
PyObject *tmp_item;
402+
long winsz_0, winsz_1;
403+
tmp_item = PySequence_GetItem(winsz, 0);
404+
winsz_0 = PyLong_AsLong(tmp_item);
405+
if (winsz_0 == -1 && PyErr_Occurred()) {
406+
Py_XDECREF(tmp_item);
407+
return NULL;
408+
}
409+
Py_XDECREF(tmp_item);
410+
tmp_item = PySequence_GetItem(winsz, 1);
411+
winsz_1 = PyLong_AsLong(tmp_item);
412+
if (winsz_1 == -1 && PyErr_Occurred()) {
413+
Py_XDECREF(tmp_item);
414+
return NULL;
415+
}
416+
Py_XDECREF(tmp_item);
417+
418+
termiosmodulestate *state = PyModule_GetState(module);
419+
420+
#if defined(TIOCGWINSZ) && defined(TIOCSWINSZ)
421+
struct winsize w;
422+
/* Get the old winsize because it might have
423+
more fields such as xpixel, ypixel. */
424+
if (ioctl(fd, TIOCGWINSZ, &w) == -1) {
425+
return PyErr_SetFromErrno(state->TermiosError);
426+
}
427+
428+
w.ws_row = (unsigned short) winsz_0;
429+
w.ws_col = (unsigned short) winsz_1;
430+
if ((((long)w.ws_row) != winsz_0) || (((long)w.ws_col) != winsz_1)) {
431+
PyErr_SetString(PyExc_OverflowError,
432+
"winsize value(s) out of range.");
433+
return NULL;
434+
}
435+
436+
if (ioctl(fd, TIOCSWINSZ, &w) == -1) {
437+
return PyErr_SetFromErrno(state->TermiosError);
438+
}
439+
440+
Py_RETURN_NONE;
441+
#elif defined(TIOCGSIZE) && defined(TIOCSSIZE)
442+
struct ttysize s;
443+
/* Get the old ttysize because it might have more fields. */
444+
if (ioctl(fd, TIOCGSIZE, &s) == -1) {
445+
return PyErr_SetFromErrno(state->TermiosError);
446+
}
447+
448+
s.ts_lines = (int) winsz_0;
449+
s.ts_cols = (int) winsz_1;
450+
if ((((long)s.ts_lines) != winsz_0) || (((long)s.ts_cols) != winsz_1)) {
451+
PyErr_SetString(PyExc_OverflowError,
452+
"winsize value(s) out of range.");
453+
return NULL;
454+
}
455+
456+
if (ioctl(fd, TIOCSSIZE, &s) == -1) {
457+
return PyErr_SetFromErrno(state->TermiosError);
458+
}
459+
460+
Py_RETURN_NONE;
461+
#else
462+
PyErr_SetString(PyExc_NotImplementedError,
463+
"requires termios.TIOCGWINSZ, termios.TIOCSWINSZ and/or termios.TIOCGSIZE, termios.TIOCSSIZE");
464+
return NULL;
465+
#endif /* defined(TIOCGWINSZ) && defined(TIOCSWINSZ) */
466+
}
467+
318468
static PyMethodDef termios_methods[] =
319469
{
320470
TERMIOS_TCGETATTR_METHODDEF
@@ -323,6 +473,8 @@ static PyMethodDef termios_methods[] =
323473
TERMIOS_TCDRAIN_METHODDEF
324474
TERMIOS_TCFLUSH_METHODDEF
325475
TERMIOS_TCFLOW_METHODDEF
476+
TERMIOS_TCGETWINSIZE_METHODDEF
477+
TERMIOS_TCSETWINSIZE_METHODDEF
326478
{NULL, NULL}
327479
};
328480

@@ -841,6 +993,9 @@ static struct constant {
841993
#ifdef TIOCGSERIAL
842994
{"TIOCGSERIAL", TIOCGSERIAL},
843995
#endif
996+
#ifdef TIOCGSIZE
997+
{"TIOCGSIZE", TIOCGSIZE},
998+
#endif
844999
#ifdef TIOCGSOFTCAR
8451000
{"TIOCGSOFTCAR", TIOCGSOFTCAR},
8461001
#endif
@@ -973,6 +1128,9 @@ static struct constant {
9731128
#ifdef TIOCSSERIAL
9741129
{"TIOCSSERIAL", TIOCSSERIAL},
9751130
#endif
1131+
#ifdef TIOCSSIZE
1132+
{"TIOCSSIZE", TIOCSSIZE},
1133+
#endif
9761134
#ifdef TIOCSSOFTCAR
9771135
{"TIOCSSOFTCAR", TIOCSSOFTCAR},
9781136
#endif

0 commit comments

Comments
 (0)