Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvs #2

Merged
merged 2 commits into from
Jan 11, 2022
Merged

Improvs #2

merged 2 commits into from
Jan 11, 2022

Conversation

eylles
Copy link
Owner

@eylles eylles commented Jan 2, 2022

this is a branch i was working on trying to add stuff from lsix to have detection of terminal size in cols and pixels, from looking at how it gets the terminal properties.

the change to bash is since i have not found any way to properly query the properties and parse them in pure posix shell, another important this is the need to check the terminal cells and rows against the X and Y pixels to calculate the X and Y pixel density of each cell for proper sixel sizing, also getting the background to properly display the images with montage on a properly colored background.

the commands to detect the properties along some echos to show them:

#!/bin/bash

timeout=0.25  # How long to wait for terminal to respond
              # to a control sequence (in seconds).

# Various terminal automatic configuration routines.

# Don't show escape sequences the terminal doesn't understand.
stty -echo			# Hush-a Mandara Ni Pari

# IS TERMINAL SIXEL CAPABLE?		# Send Device Attributes
old_ifs=$IFS
IFS=";" read -a REPLY -s -t 1 -d "c" -p $'\e[c' >&2
for code in "${REPLY[@]}"; do
  if [[ $code == "4" ]]; then
    echo "$code"
    hassixel=yup
    echo "$hassixel"
    # break
  fi
done
IFS=$old_ifs

# Query the terminal background and foreground colors.
old_ifs=$IFS
IFS=";:/"  read -a REPLY -r -s -t ${timeout} -d "\\" -p $'\e]11;?\e\\' >&2
if [[ ${REPLY[1]} =~ ^rgb ]]; then
  # Return value format: $'\e]11;rgb:ffff/0000/ffff\e\\'.
  # ImageMagick wants colors formatted as #ffff0000ffff.
  background='#'${REPLY[2]}${REPLY[3]}${REPLY[4]%%$'\e'*}
fi
IFS=$old_ifs

echo "$background"

# Send control sequence to query the sixel graphics geometry to
# find out how large of a sixel image can be shown.
old_ifs=$IFS
IFS=";"  read -a REPLY -s -t ${timeout} -d "S" -p $'\e[?2;1;0S' >&2
if [[ ${REPLY[2]} -gt 0 ]]; then
  width=${REPLY[2]}
  height=${REPLY[3]}
fi
IFS=$old_ifs
# else

# Nope. Fall back to dtterm WindowOps to approximate sixel geometry.
old_ifs=$IFS
IFS=";" read -a REPLY -s -t ${timeout} -d "t" -p $'\e[14t' >&2
winwidth=${REPLY[2]}"x"${REPLY[1]}
IFS=$old_ifs
# fi

# returns ^[4;height;widtht
old_ifs=$IFS
IFS=";" read -a REPLY -s -t ${timeout} -d "t" -p $'\e[19t' >&2
dim=${REPLY[2]}"x"${REPLY[1]}
IFS=$old_ifs

echo "$width"
echo "$height"
echo "$winwidth"

echo "$dim"

a lot of this was taken directly from lsix

ideally some of these could be cached to not have the need for constantly queriying the properties on every image.

@eylles
Copy link
Owner Author

eylles commented Jan 2, 2022

@trappedinspacetime this is some work i had pending but didn't push because i was busy with other stuff.

@eylles
Copy link
Owner Author

eylles commented Jan 11, 2022

Hmmm it may be possible to not use bash, apparently modification of stty settings can be used to read answer sequences, but i'm too smooth brained to understand well what is going on.

@trappedinspacetime
Copy link
Collaborator

trappedinspacetime commented Jan 11, 2022

@eylles I'm not a bash expert but I like using bash scripts. I prefer using bash in scripting. I came across the following bug hackerb9/lsix#54, so I didn't get far. I also have a health issue, so I may lag behind.
I met an image-viewer script here vifm/vifm#419 (comment)

@trappedinspacetime trappedinspacetime merged commit 64b943d into master Jan 11, 2022
@hackerb9
Copy link

Hmmm it may be possible to not use bash, apparently modification of stty settings can be used to read answer sequences, but i'm too smooth brained to understand well what is going on.

It's possible that not understanding stty is actually a sign that your brain is healthy.

Back in the olden days, before we had bash to do the heavy lifting, we used extremely ugly kludges via the stty command in the Bourne shell. However, stty was so brittle that people would often write a custom routine in C to handle the termios ioctl.

To get a flavour of what we had to do, here's an implementation I wrote in Python:

## See man termios(3) for details on tcgetattr
from termios import *
def terminal_query(seq, delimiter=None, timeout=0.2):
    """
    Given an escape SEQuence, and optionally a DELIMITER and a TIMEOUT,
    print SEQ to stderr and read a response from stdin until the
    character DELIMITER is read or TIMEOUT seconds is reached. 
    Input that is read is returned to the calling function. 
    If TIMEOUT is not specified, it default to 0.2.
    If DELIMITER is not specified, it defaults to the last character of SEQ.
    If neither DELIMITER nor SEQ are specified, then "" is returned.
    """
    import sys, copy, posix

    if not delimiter and not seq:
        return ""

    if not seq: seq=""          # Allow simply reading from terminal.

    # Responses usually end with the same character as the request.
    if not delimiter and len(seq)>0:
        delimiter=seq[-1]

    oldmode = tcgetattr(sys.stdin.fileno())

    # tcgetattr returns [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
    [ iflag, oflag, cflag, lflag, ispeed, ospeed, cc ]  = oldmode

    ## CBREAK MODE: Read byte by byte.
    ## Cbreak is like Raw but allow ^C interrupt signal and does
    ## not clear OPOST (print newlines as carriage return + newline)

    # Do not transmogrify input in any way...
    iflag = iflag & ~(INPCK | ISTRIP | IXON | ICRNL | INLCR | IGNCR)
    # ... except do allow BREAK (^C) to flush queues and send SIGINT
    iflag = (iflag & ~IGNBRK) | BRKINT

    lflag = lflag & ~ICANON     # Noncanonical: read by bytes, not lines
    lflag = lflag & ~ECHO       # Do not echo characters received

    # Clear character size and disable parity checking
    cflag = cflag & ~(CSIZE | PARENB)
    cflag = cflag | CS8        # Set 8-bit character size

    ## Polling read (MIN == 0, TIME == 0)  See termios(3).
    cc[VMIN] = 0                
    cc[VTIME] = 0
    pollingread = copy.deepcopy([ iflag, oflag, cflag, lflag, ispeed, ospeed, cc ])
    
    ## Read with timeout (MIN == 0, TIME > 0)  See termios(3).
    cc[VMIN] = 0                
    cc[VTIME] = int(timeout*10+0.5) # Timeout is in tenths of a second 
    readwithtimeout = copy.deepcopy([ iflag, oflag, cflag, lflag, ispeed, ospeed, cc ])

    # Drain stdin in case there's junk from a prev req in there already. 
    tcsetattr(sys.stdin.fileno(), TCSANOW, pollingread)
    while posix.read(sys.stdin.fileno(), 1024):
        debugprint(".", end='')

    output=""                   # String of output so far.
    c = None                    # Currently read character.
    try:
        # Next read() should timeout if no byte becomes available.
        tcsetattr(sys.stdin.fileno(), TCSANOW, readwithtimeout)

        print(seq, file=stderr, end='', flush=True) # Send Esc seq to terminal

        # Accumulate response in 'output' until delimiter or timeout
        while c != delimiter:
            c = posix.read(sys.stdin.fileno(), 1).decode()
            if c:
                output=output+c
#                debugprint("got", repr(c), "waiting for", repr(delimiter))
            else:
                debugprint("read() returned 0 characters after", repr(seq))
                # Timeout
                break
    finally:
        tcsetattr(sys.stdin.fileno(), TCSANOW, oldmode)

    if (debug):
        if output:
            debugprint("Terminal query received: ", repr(output))

        if (c == delimiter):
            debugprint("Exited on delimiter", repr(c))
        else:
            debugprint("Exited after timeout", repr(c))

    return(output)

While it works, I am thankful that bash exists and I don't have to wrap my brain around such ugliness anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants