Skip to content

Suggestion: use stderr for I/O, or make it configurable #47

@semicontinuity

Description

@semicontinuity

Hello, here is a suggestion: it would be nice if picotui used stderr for I/O, and not stdin+stdout.
Why? that would free stdio and stdout for other application-specific things: the app can get its input from stdin and write result to stdout, unix-way, and it would allow to use picotui-base app in unix pipes:
echo "input-data" | picotui-app | consumer

I'm doing exactly that in my picotui-based app, and it works great.
Here's the code I had to add to make it work (thanks to python, no need to modify picotui code, can just patch it):

from picotui.screen import Screen

#################################################################################
# Start patching picotui to use STDERR (FD 2)
#################################################################################
FD_IN = 2
FD_OUT = 2


import os


def wr(*args):
    s = args[-1]
    # TODO: When Python is 3.5, update this to use only bytes
    if isinstance(s, str):
        s = bytes(s, "utf-8")
    os.write(FD_OUT, s)


from picotui.basewidget import Widget
from picotui.defs import KEYMAP as _KEYMAP


def get_input(self):
    if self.kbuf:
        key = self.kbuf[0:1]
        self.kbuf = self.kbuf[1:]
    else:
        key = os.read(FD_IN, 32)
        if key[0] != 0x1b:
            key = key.decode()
            self.kbuf = key[1:].encode()
            key = key[0:1].encode()
    key = _KEYMAP.get(key, key)

    if isinstance(key, bytes) and key.startswith(b"\x1b[M") and len(key) == 6:
        row = key[5] - 33
        col = key[4] - 33
        return [col, row]

    return key


def screen_size(*args):
    import select
    wr(b"\x1b[18t")
    res = select.select([FD_IN], [], [], 0.2)[0]
    if not res:
        return (80, 24)
    resp = os.read(FD_IN, 32)
    assert resp.startswith(b"\x1b[8;") and resp[-1:] == b"t"
    vals = resp[:-1].split(b";")
    return (int(vals[2]), int(vals[1]))


def init_tty(*args):
    import tty, termios
    global ttyattr
    ttyattr = termios.tcgetattr(FD_IN)
    tty.setraw(FD_IN)


def deinit_tty(*args):
    import termios
    termios.tcsetattr(FD_IN, termios.TCSANOW, ttyattr)


def patch_picotui():
    Screen.wr = wr
    Screen.init_tty = init_tty
    Screen.deinit_tty = deinit_tty
    Screen.screen_size = screen_size
    Widget.get_input = get_input

#################################################################################
# End patching picotui
#################################################################################

.. that's it, just introduced constants instead of hard-coded FD 0 and 1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions