Skip to content

Auto inline images #302

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

Open
wants to merge 1 commit 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
43 changes: 42 additions & 1 deletion image.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ static int image_index = 0;

/* display image */

typedef struct _termialImage {
typedef struct _terminalImage {
ImageCache *cache;
short x;
short y;
Expand All @@ -34,10 +34,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 +138,37 @@ openImgdisplay()
return FALSE;
}

static int
inline_img_protocol_autodetect(void)
{
int result;
const char *env_term, *konsole_version;

if ((env_term = getenv("TERM"))) {
if (strcmp(env_term, "xterm-kitty") == 0)
return INLINE_IMG_KITTY;
if (strcmp(env_term, "xterm-ghostty") == 0)
return INLINE_IMG_KITTY;
/* yaft doesn't correctly respond to \e[c, but is sixel-capable
* anyway. Thanks to hackerb9/lsix */
if (strncmp(env_term, "yaft", 4) == 0)
return INLINE_IMG_SIXEL;
}

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

if ((result = img_protocol_test_for_sixel()) != INLINE_IMG_NONE)
return result;

/* If mlterm too old to support sixel, probably supports older OSC5379 */
if (env_term && strncmp(env_term, "mlterm", 6) == 0)
return INLINE_IMG_OSC5379;

return INLINE_IMG_NONE;
}

static void
closeImgdisplay(void)
{
Expand Down Expand Up @@ -256,6 +292,11 @@ 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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is P_CHARINT needed here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because P_INT does not accept negative values.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, it's ignored when setting the value. I only looked at reading it... Bad naming though...

But then OK.

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
111 changes: 111 additions & 0 deletions terms.c
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,117 @@ put_image_sixel(char *url, int x, int y, int w, int h, int sx, int sy, int sw, i
MOVE(Currentbuf->cursorY,Currentbuf->cursorX);
}

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
*/
int
img_protocol_test_for_sixel(void)
{
static const char errstr[] = "Can't get terminal attributes";
size_t response_size = 256, len = 0;
char *response = GC_MALLOC_ATOMIC(response_size);

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

/* loop until we get the whole response */
for (; response; response = GC_REALLOC(response, response_size *= 2)) {
ssize_t nchars_read;
int nret;
fd_set fds;
/* wait 0.2s (from get_pixel_per_char()) for input */
struct timeval timeout = { 0, 0.2 * 1000000 };
FD_ZERO(&fds);
FD_SET(tty, &fds);
if ((nret = select(tty + 1, &fds, NULL, NULL, &timeout)) <= 0) {
fprintf(stderr, "%s: %s\n",
errstr, nret == 0 ? "timed out" : strerror(errno));
return INLINE_IMG_NONE;
}

nchars_read = read(tty, response + len, response_size - len - 1);
if (nchars_read < 0) {
perror(errstr);
return INLINE_IMG_NONE;
}

/* first useful iteration; validate response */
if (nchars_read + len >= 3 &&
(memcmp(response, "\033[", 2) != 0 ||
(response[2] != '?' && response[2] != '=')))
{
fputs("Malformed terminal attributes\n", stderr);
return INLINE_IMG_NONE;
}

/* NUL-terminate the terminal response */
{
char *ptr = memchr(response + len, 'c', nchars_read);
if (ptr) {
ptr[1] = '\0';
break;
}
}

/* if we can't find 'c', then the response is incomplete; try again */
len += (size_t)nchars_read;
}

/* separate the response parameters by ';' and look for '4' */
if (response) {
char *ptr;
if (response[2] == '=')
return INLINE_IMG_NONE; /* SyncTERM */
for (ptr = response; (ptr = strchr(ptr, ';'));) {
if (*++ptr != '4')
continue;
switch (*++ptr) {
case ';':
case 'c':
if (have_img2sixel())
return INLINE_IMG_SIXEL;
}
}
} else
perror(errstr);

/* default */
return INLINE_IMG_NONE;
}

int
get_pixel_per_cell(int *ppc, int *ppl)
{
Expand Down
1 change: 1 addition & 0 deletions terms.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extern void put_image_osc5379(char *url, int x, int y, int w, int h, int sx, int
extern void put_image_sixel(char *url, int x, int y, int w, int h, int sx, int sy, int sw, int sh, int n_terminal_image);
extern void put_image_iterm2(char *url, int x, int y, int w, int h);
extern void put_image_kitty(char *url, int x, int y, int w, int h, int sx, int sy, int sw, int sh, int c, int r);
extern int img_protocol_test_for_sixel(void);
extern int get_pixel_per_cell(int *ppc, int *ppl);
#endif

Expand Down