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

Auto inline images #302

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion display.c
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,14 @@ drawAnchorCursor0(Buffer *buf, AnchorList *al, int hseq, int prevhseq,
int start_pos = an->start.pos;
int end_pos = an->end.pos;
for (i = an->start.pos; i < an->end.pos; i++) {
if (enable_inline_image && (l->propBuf[i] & PE_IMAGE)) {
#ifdef USE_IMAGE
if (enable_inline_image && (l->propBuf[i] & PE_IMAGE)) {
if (start_pos == i)
start_pos = i + 1;
else if (end_pos == an->end.pos)
end_pos = i - 1;
}
#endif
if (l->propBuf[i] & (PE_IMAGE | PE_ANCHOR | PE_FORM)) {
if (active)
l->propBuf[i] |= PE_ACTIVE;
Expand Down
8 changes: 7 additions & 1 deletion fm.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,14 @@ extern int REV_LB[];
#define EOL(l) (&(l)->ptr[(l)->length])
#define IS_EOL(p,l) ((p)==&(l)->ptr[(l)->length])

#ifdef USE_IMAGE
#define INLINE_IMG_AUTO -1
#define INLINE_IMG_NONE 0
#define INLINE_IMG_OSC5379 1
#define INLINE_IMG_SIXEL 2
#define INLINE_IMG_ITERM2 3
#define INLINE_IMG_KITTY 4
#endif

/*
* Types.
Expand Down Expand Up @@ -940,7 +943,10 @@ global char *CurrentKeyData;
global char *CurrentCmdData;
global char *w3m_reqlog;
extern char *w3m_version;
extern int enable_inline_image;
#ifdef USE_IMAGE
global unsigned char enable_inline_image init(INLINE_IMG_NONE);
global signed char enable_inline_image_config init(INLINE_IMG_AUTO);
#endif

#define DUMP_BUFFER 0x01
#define DUMP_HEAD 0x02
Expand Down
149 changes: 148 additions & 1 deletion image.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@
#ifdef HAVE_WAITPID
#include <sys/wait.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#else
#include <sys/time.h>
#endif /* HAVE_SYS_SELECT_H */

#ifdef USE_IMAGE

static int image_index = 0;

/* display image */

typedef struct _termialImage {
typedef struct _terminalImage {
ImageCache *cache;
short x;
short y;
Expand All @@ -34,10 +39,15 @@ static pid_t Imgdisplay_pid = 0;
static int openImgdisplay(void);
static void closeImgdisplay(void);
static int getCharSize(void);
static int inline_img_protocol_autodetect(void);

void
initImage()
{
enable_inline_image = enable_inline_image_config == INLINE_IMG_AUTO
? inline_img_protocol_autodetect()
: enable_inline_image_config;

if (activeImage)
return;
if (getCharSize())
Expand Down Expand Up @@ -133,6 +143,139 @@ openImgdisplay()
return FALSE;
}

static int
protocol_test_environment(void)
{
const char * envptr;

if ((envptr = getenv("TERM"))) {
/* The purpose of strncmp(3) where used below is like the glob `prefix*',
* or the regex `^prefix' */
if (strcmp(envptr, "xterm-kitty") == 0)
return INLINE_IMG_KITTY;
if (strncmp(envptr, "mlterm", 6) == 0)
return INLINE_IMG_OSC5379;
/* yaft doesn't correctly respond to \e[c, but is sixel-capable
* anyway. Thanks to hackerb9/lsix */
if (strncmp(envptr, "yaft", 4) == 0)
return INLINE_IMG_SIXEL;
}

if ((envptr = getenv("KONSOLE_VERSION")) && strcmp(envptr, "220770") >= 0)
return INLINE_IMG_KITTY;

return INLINE_IMG_NONE;
}

static int
have_img2sixel(void)
{
pid_t child_pid;
int wstatus;

if (getenv("W3M_IMG2SIXEL"))
return TRUE;

switch (child_pid = fork()) {
case -1:
return FALSE;
case 0:
close(STDOUT_FILENO);
close(STDERR_FILENO);
execlp("img2sixel", "img2sixel", "--version", NULL);
/* if exec fails */
_exit(-1);
default:
return
#ifdef HAVE_WAITPID
waitpid(child_pid, &wstatus, 0) != -1
#else
wait(&wstatus) != -1
#endif
&& WIFEXITED(wstatus)
&& WEXITSTATUS(wstatus) == 0;
}
}

/* NB: in theory, user input could be snarfed up with the read(2); in
* practice, only museum pieces have such low latency, so we should get
* the device attributes (and only the device attributes) immediately. If
* ever this becomes an issue (which it won't), ungetch(3) can be used on
* any extraneous input */
static int
protocol_test_for_sixel(void)
{
static const char errstr[] = "Can't get terminal attributes";

/* the string in the following sizeof expression is what I think the
* largest possible response could be. See
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
* for more information, specifically the section on CSI Ps c ("\e[c") */
char term_response[sizeof "\033[?65;1;2;3;4;6;8;9;15;16;17;18;21;22;28;29c"] = "";
char * ptr; /* general-purpose pointer to the above buffer */
ssize_t nchars_read;

/* request tty send primary device attributes */
write(STDOUT_FILENO, "\033[c", 3);

/* wait for stdin to become available; don't let us get stuck hanging
* indefinitely on a response that won't come */
{
int nret;
fd_set fds;
struct timeval timeout = { 0, 0.2 * 1000000 }; /* wait 0.2s, from terms.c */
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
if ((nret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &timeout)) <= 0) {
fprintf(stderr, "%s: %s\n", errstr, nret == 0 ? "timed out" : strerror(errno));
return INLINE_IMG_NONE;
}
}

if ((nchars_read = read(STDIN_FILENO, term_response, sizeof term_response - 1)) == -1) {
perror(errstr);
return INLINE_IMG_NONE;
}

/* NUL-terminate overall response */
term_response[nchars_read] = '\0';

/* validate response */
if (nchars_read < 5
|| memcmp(term_response, "\033[?", 3) != 0
|| !(ptr = memchr(term_response, 'c', nchars_read)))
{
fputs("Malformed terminal attributes\n", stderr);
return INLINE_IMG_NONE;
}

/* NUL-terminate the specific portion of data that is the terminal response */
if (ptr[1])
ptr[1] = '\0';

/* separate the response parameters by ';' and look for '4' */
ptr = term_response;
while ((ptr = strchr(ptr, ';'))) {
if (*++ptr != '4')
continue;
switch (*++ptr) {
case ';': case 'c':
if (have_img2sixel())
return INLINE_IMG_SIXEL;
}
}

/* default */
return INLINE_IMG_NONE;
}

static int
inline_img_protocol_autodetect(void)
{
int result = protocol_test_environment();
return result ? result : protocol_test_for_sixel();
}

static void
closeImgdisplay(void)
{
Expand Down Expand Up @@ -256,6 +399,10 @@ drawImage(void)
put_image_iterm2(url, x, y, sw, sh);
} else if (enable_inline_image == INLINE_IMG_KITTY) {
put_image_kitty(url, x, y, i->width, i->height, i->sx, i->sy, sw * pixel_per_char, sh * pixel_per_line_i, sw, sh);
#ifdef DEBUG
} else {
fprintf(stderr, "Unrecognised inline image protocol: %d\n", enable_inline_image);
#endif
}

continue ;
Expand Down
6 changes: 3 additions & 3 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ static int searchKeyNum(void);
#define help() fusage(stdout, 0)
#define usage() fusage(stderr, 1)

int enable_inline_image;

static void
fversion(FILE * f)
{
Expand Down Expand Up @@ -719,13 +717,13 @@ main(int argc, char **argv)
set_pixel_per_line = TRUE;
}
}
#endif
else if (!strcmp("-ri", argv[i])) {
enable_inline_image = INLINE_IMG_OSC5379;
}
else if (!strcmp("-sixel", argv[i])) {
enable_inline_image = INLINE_IMG_SIXEL;
}
#endif
else if (!strcmp("-num", argv[i]))
showLineNum = TRUE;
else if (!strcmp("-no-proxy", argv[i]))
Expand Down Expand Up @@ -6006,11 +6004,13 @@ deleteFiles()
}
while ((f = popText(fileToDelete)) != NULL) {
unlink(f);
#ifdef USE_IMAGE
if (enable_inline_image == INLINE_IMG_SIXEL && strcmp(f+strlen(f)-4, ".gif") == 0) {
Str firstframe = Strnew_charp(f);
Strcat_charp(firstframe, "-1");
unlink(firstframe->ptr);
}
#endif
}
}

Expand Down
5 changes: 3 additions & 2 deletions rc.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ static struct sel_c graphic_char_str[] = {

#ifdef USE_IMAGE
static struct sel_c inlineimgstr[] = {
{N_S(INLINE_IMG_AUTO), N_("auto-select protocol")},
{N_S(INLINE_IMG_NONE), N_("external command")},
{N_S(INLINE_IMG_OSC5379), N_("OSC 5379 (mlterm)")},
{N_S(INLINE_IMG_SIXEL), N_("sixel (img2sixel)")},
Expand Down Expand Up @@ -449,7 +450,7 @@ struct param_ptr params1[] = {
CMT_EXT_IMAGE_VIEWER, NULL},
{"image_scale", P_SCALE, PI_TEXT, (void *)&image_scale, CMT_IMAGE_SCALE,
NULL},
{"inline_img_protocol", P_INT, PI_SEL_C, (void *)&enable_inline_image,
{"inline_img_protocol", P_CHARINT, PI_SEL_C, (void *)&enable_inline_image_config,
CMT_INLINE_IMG_PROTOCOL, (void *)inlineimgstr},
{"imgdisplay", P_STRING, PI_TEXT, (void *)&Imgdisplay, CMT_IMGDISPLAY,
NULL},
Expand Down Expand Up @@ -1313,7 +1314,7 @@ sync_with_option(void)
init_migemo();
#endif
#ifdef USE_IMAGE
if (fmInitialized && (displayImage || enable_inline_image))
if (fmInitialized && (displayImage || enable_inline_image_config))
initImage();
#else
displayImage = FALSE; /* XXX */
Expand Down