diff --git a/doc/json_schema.json b/doc/json_schema.json index efb530e7d..3d18d62c1 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -432,6 +432,7 @@ "sixel", "kitty", "kitty-direct", + "kitty-icat", "iterm", "chafa", "raw", diff --git a/src/data/help.json b/src/data/help.json index 481b3a3e2..de0fbca7b 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -186,7 +186,8 @@ "data-raw": "Text data, printed as is", "sixel": "Image file, printed as sixel codes", "kitty": "Image file, printed as kitty graphics protocol", - "kitty_direct": "Image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)", + "kitty-direct": "Image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)", + "kitty-icat": "Image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed", "iterm": "Image file, printed as iterm graphics protocol", "chafa": "Image file, printed as ascii art using libchafa", "raw": "Image file, printed as raw binary string", diff --git a/src/logo/image/image.c b/src/logo/image/image.c index 25cca39f2..cc8f2129e 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -1,6 +1,7 @@ #include "image.h" #include "common/io/io.h" #include "common/printing.h" +#include "common/processing.h" #include "util/stringUtils.h" #include "util/base64.h" #include "detection/terminalsize/terminalsize.h" @@ -138,6 +139,126 @@ static bool printImageIterm(bool printError) return true; } +static bool printImageKittyIcat(bool printError) +{ + const FFOptionsLogo* options = &instance.config.logo; + + if (!ffPathExists(options->source.chars, FF_PATHTYPE_FILE)) + { + if (printError) + fputs("Logo (kitty-icat): Failed to load image file\n", stderr); + return false; + } + + fflush(stdout); + + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); + + if (options->position == FF_LOGO_POSITION_LEFT) + { + ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop + 1, + (unsigned) options->paddingLeft + 1 + ); + } + else if (options->position == FF_LOGO_POSITION_TOP) + { + if (!options->width) + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + } + else + { + if (printError) + fputs("Logo (kitty-icat): position top is not supported when logo width is set\n", stderr); + return false; + } + } + else if (options->position == FF_LOGO_POSITION_RIGHT) + { + if (printError) + fputs("Logo (kitty-icat): position right is not supported\n", stderr); + return false; + } + + uint32_t prevLength = buf.length; + + const char* error = NULL; + + if (options->width) + { + char place[64]; + snprintf(place, + ARRAY_SIZE(place), + "--place=%ux9999@%ux%u", + options->width, + options->paddingLeft + 1, + options->paddingTop + 1); + + error = ffProcessAppendStdOut(&buf, (char* []) { + "kitten", + "icat", + "-n", + "--align=left", + place, + "--scale-up", + options->source.chars, + NULL, + }); + } + else + { + error = ffProcessAppendStdOut(&buf, (char* []) { + "kitten", + "icat", + "-n", + "--align=left", + options->source.chars, + NULL, + }); + } + if (error) + { + if (printError) + fprintf(stderr, "Logo (kitty-icat): running `kitten icat` failed %s\n", error); + return false; + } + + if (buf.length == prevLength) + { + if (printError) + fputs("Logo (kitty-icat): `kitten icat` returned empty output\n", stderr); + return false; + } + + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + + if (options->position == FF_LOGO_POSITION_LEFT || options->position == FF_LOGO_POSITION_RIGHT) + { + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", 2, "%*[^0-9]%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (kitty-icat): fail to query cursor position: %s\n", error); + return true; // We already printed image logo, don't print ascii logo then + } + if (X < options->paddingLeft + options->width) + X = (uint16_t) (options->paddingLeft + options->width); + if (options->position == FF_LOGO_POSITION_LEFT) + instance.state.logoWidth = X + options->paddingRight - 1; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else if (options->position == FF_LOGO_POSITION_TOP) + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } + + return true; +} + static bool printImageKittyDirect(bool printError) { const FFOptionsLogo* options = &instance.config.logo; @@ -957,6 +1078,9 @@ bool ffLogoPrintImageIfExists(FFLogoType type, bool printError) if(type == FF_LOGO_TYPE_IMAGE_KITTY_DIRECT) return printImageKittyDirect(printError); + if(type == FF_LOGO_TYPE_IMAGE_KITTY_ICAT) + return printImageKittyIcat(printError); + #if !defined(FF_HAVE_CHAFA) if(type == FF_LOGO_TYPE_IMAGE_CHAFA) { diff --git a/src/options/logo.c b/src/options/logo.c index 582644c7f..7b2d10673 100644 --- a/src/options/logo.c +++ b/src/options/logo.c @@ -63,6 +63,7 @@ logoType: { "sixel", FF_LOGO_TYPE_IMAGE_SIXEL }, { "kitty", FF_LOGO_TYPE_IMAGE_KITTY }, { "kitty-direct", FF_LOGO_TYPE_IMAGE_KITTY_DIRECT }, + { "kitty-icat", FF_LOGO_TYPE_IMAGE_KITTY_ICAT }, { "iterm", FF_LOGO_TYPE_IMAGE_ITERM }, { "chafa", FF_LOGO_TYPE_IMAGE_CHAFA }, { "raw", FF_LOGO_TYPE_IMAGE_RAW }, @@ -283,6 +284,7 @@ const char* ffOptionsParseLogoJsonConfig(FFOptionsLogo* options, yyjson_val* roo { "sixel", FF_LOGO_TYPE_IMAGE_SIXEL }, { "kitty", FF_LOGO_TYPE_IMAGE_KITTY }, { "kitty-direct", FF_LOGO_TYPE_IMAGE_KITTY_DIRECT }, + { "kitty-icat", FF_LOGO_TYPE_IMAGE_KITTY_ICAT }, { "iterm", FF_LOGO_TYPE_IMAGE_ITERM }, { "chafa", FF_LOGO_TYPE_IMAGE_CHAFA }, { "raw", FF_LOGO_TYPE_IMAGE_RAW }, diff --git a/src/options/logo.h b/src/options/logo.h index a4a031d5f..c2a180b7b 100644 --- a/src/options/logo.h +++ b/src/options/logo.h @@ -17,6 +17,7 @@ typedef enum __attribute__((__packed__)) FFLogoType FF_LOGO_TYPE_IMAGE_SIXEL, //image file, printed as sixel codes FF_LOGO_TYPE_IMAGE_KITTY, //image file, printed as kitty graphics protocol FF_LOGO_TYPE_IMAGE_KITTY_DIRECT, //image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm) + FF_LOGO_TYPE_IMAGE_KITTY_ICAT, //image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed" FF_LOGO_TYPE_IMAGE_ITERM, //image file, printed as iterm graphics protocol FF_LOGO_TYPE_IMAGE_CHAFA, //image file, printed as ascii art using libchafa FF_LOGO_TYPE_IMAGE_RAW, //image file, printed as raw binary string