diff options
| author | Antoine Pitrou <solipsis@pitrou.net> | 2012-02-08 23:28:36 +0100 | 
|---|---|---|
| committer | Antoine Pitrou <solipsis@pitrou.net> | 2012-02-08 23:28:36 +0100 | 
| commit | bcf2b59fb5f18c09a26da3e9b60a37367f2a28ba (patch) | |
| tree | 47232d9eb97758190b44700163d2706665224d7c /Modules/posixmodule.c | |
| parent | 4195b5caea0fe1446160e78d69420732ead7e78b (diff) | |
| download | cpython-git-bcf2b59fb5f18c09a26da3e9b60a37367f2a28ba.tar.gz | |
Issue #13609: Add two functions to query the terminal size:
os.get_terminal_size (low level) and shutil.get_terminal_size (high level).
Patch by Zbigniew Jędrzejewski-Szmek.
Diffstat (limited to 'Modules/posixmodule.c')
| -rw-r--r-- | Modules/posixmodule.c | 130 | 
1 files changed, 130 insertions, 0 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 8b2b2117cb..0553c761bd 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -125,6 +125,18 @@ corresponding Unix manual entries for more information on calls.");  #include <dlfcn.h>  #endif +#if defined(MS_WINDOWS) +#  define TERMSIZE_USE_CONIO +#elif defined(HAVE_SYS_IOCTL_H) +#  include <sys/ioctl.h> +#  if defined(HAVE_TERMIOS_H) +#    include <termios.h> +#  endif +#  if defined(TIOCGWINSZ) +#    define TERMSIZE_USE_IOCTL +#  endif +#endif /* MS_WINDOWS */ +  /* Various compilers have only certain posix functions */  /* XXX Gosh I wish these were all moved into pyconfig.h */  #if defined(PYCC_VACPP) && defined(PYOS_OS2) @@ -10477,6 +10489,114 @@ posix_flistxattr(PyObject *self, PyObject *args)  #endif /* USE_XATTRS */ + +/* Terminal size querying */ + +static PyTypeObject TerminalSizeType; + +PyDoc_STRVAR(TerminalSize_docstring, +    "A tuple of (columns, lines) for holding terminal window size"); + +static PyStructSequence_Field TerminalSize_fields[] = { +    {"columns", "width of the terminal window in characters"}, +    {"lines", "height of the terminal window in characters"}, +    {NULL, NULL} +}; + +static PyStructSequence_Desc TerminalSize_desc = { +    "os.terminal_size", +    TerminalSize_docstring, +    TerminalSize_fields, +    2, +}; + +#if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) +PyDoc_STRVAR(termsize__doc__, +    "Return the size of the terminal window as (columns, lines).\n"        \ +    "\n"                                                                   \ +    "The optional argument fd (default standard output) specifies\n"       \ +    "which file descriptor should be queried.\n"                           \ +    "\n"                                                                   \ +    "If the file descriptor is not connected to a terminal, an OSError\n"  \ +    "is thrown.\n"                                                         \ +    "\n"                                                                   \ +    "This function will only be defined if an implementation is\n"         \ +    "available for this system.\n"                                         \ +    "\n"                                                                   \ +    "shutil.get_terminal_size is the high-level function which should \n"  \ +    "normally be used, os.get_terminal_size is the low-level implementation."); + +static PyObject* +get_terminal_size(PyObject *self, PyObject *args) +{ +    int columns, lines; +    PyObject *termsize; + +    int fd = fileno(stdout); +    /* Under some conditions stdout may not be connected and +     * fileno(stdout) may point to an invalid file descriptor. For example +     * GUI apps don't have valid standard streams by default. +     * +     * If this happens, and the optional fd argument is not present, +     * the ioctl below will fail returning EBADF. This is what we want. +     */ + +    if (!PyArg_ParseTuple(args, "|i", &fd)) +        return NULL; + +#ifdef TERMSIZE_USE_IOCTL +    { +        struct winsize w; +        if (ioctl(fd, TIOCGWINSZ, &w)) +            return PyErr_SetFromErrno(PyExc_OSError); +        columns = w.ws_col; +        lines = w.ws_row; +    } +#endif /* TERMSIZE_USE_IOCTL */ + +#ifdef TERMSIZE_USE_CONIO +    { +        DWORD nhandle; +        HANDLE handle; +        CONSOLE_SCREEN_BUFFER_INFO csbi; +        switch (fd) { +        case 0: nhandle = STD_INPUT_HANDLE; +            break; +        case 1: nhandle = STD_OUTPUT_HANDLE; +            break; +        case 2: nhandle = STD_ERROR_HANDLE; +            break; +        default: +            return PyErr_Format(PyExc_ValueError, "bad file descriptor"); +        } +        handle = GetStdHandle(nhandle); +        if (handle == NULL) +            return PyErr_Format(PyExc_OSError, "handle cannot be retrieved"); +        if (handle == INVALID_HANDLE_VALUE) +            return PyErr_SetFromWindowsErr(0); + +        if (!GetConsoleScreenBufferInfo(handle, &csbi)) +            return PyErr_SetFromWindowsErr(0); + +        columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; +        lines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; +    } +#endif /* TERMSIZE_USE_CONIO */ + +    termsize = PyStructSequence_New(&TerminalSizeType); +    if (termsize == NULL) +        return NULL; +    PyStructSequence_SET_ITEM(termsize, 0, PyLong_FromLong(columns)); +    PyStructSequence_SET_ITEM(termsize, 1, PyLong_FromLong(lines)); +    if (PyErr_Occurred()) { +        Py_DECREF(termsize); +        return NULL; +    } +    return termsize; +} +#endif /* defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) */ + +  static PyMethodDef posix_methods[] = {      {"access",          posix_access, METH_VARARGS, posix_access__doc__},  #ifdef HAVE_TTYNAME @@ -10945,6 +11065,9 @@ static PyMethodDef posix_methods[] = {      {"llistxattr", posix_llistxattr, METH_VARARGS, posix_llistxattr__doc__},      {"flistxattr", posix_flistxattr, METH_VARARGS, posix_flistxattr__doc__},  #endif +#if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) +    {"get_terminal_size", get_terminal_size, METH_VARARGS, termsize__doc__}, +#endif      {NULL,              NULL}            /* Sentinel */  }; @@ -11539,6 +11662,10 @@ INITFUNC(void)          PyStructSequence_InitType(&SchedParamType, &sched_param_desc);          SchedParamType.tp_new = sched_param_new;  #endif + +        /* initialize TerminalSize_info */ +        PyStructSequence_InitType(&TerminalSizeType, &TerminalSize_desc); +        Py_INCREF(&TerminalSizeType);      }  #if defined(HAVE_WAITID) && !defined(__APPLE__)      Py_INCREF((PyObject*) &WaitidResultType); @@ -11593,6 +11720,9 @@ INITFUNC(void)  #endif /* __APPLE__ */ + +    PyModule_AddObject(m, "terminal_size", (PyObject*) &TerminalSizeType); +      return m;  }  | 
