Skip to content

Commit 9f37557

Browse files
committed
Logo (Image): support generate image data via kitten icat
1 parent aa63c4f commit 9f37557

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed

doc/json_schema.json

+1
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@
432432
"sixel",
433433
"kitty",
434434
"kitty-direct",
435+
"kitty-icat",
435436
"iterm",
436437
"chafa",
437438
"raw",

src/data/help.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@
186186
"data-raw": "Text data, printed as is",
187187
"sixel": "Image file, printed as sixel codes",
188188
"kitty": "Image file, printed as kitty graphics protocol",
189-
"kitty_direct": "Image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)",
189+
"kitty-direct": "Image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)",
190+
"kitty-icat": "Image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed",
190191
"iterm": "Image file, printed as iterm graphics protocol",
191192
"chafa": "Image file, printed as ascii art using libchafa",
192193
"raw": "Image file, printed as raw binary string",

src/logo/image/image.c

+124
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "image.h"
22
#include "common/io/io.h"
33
#include "common/printing.h"
4+
#include "common/processing.h"
45
#include "util/stringUtils.h"
56
#include "util/base64.h"
67
#include "detection/terminalsize/terminalsize.h"
@@ -138,6 +139,126 @@ static bool printImageIterm(bool printError)
138139
return true;
139140
}
140141

142+
static bool printImageKittyIcat(bool printError)
143+
{
144+
const FFOptionsLogo* options = &instance.config.logo;
145+
146+
if (!ffPathExists(options->source.chars, FF_PATHTYPE_FILE))
147+
{
148+
if (printError)
149+
fputs("Logo (kitty-icat): Failed to load image file\n", stderr);
150+
return false;
151+
}
152+
153+
fflush(stdout);
154+
155+
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
156+
157+
if (options->position == FF_LOGO_POSITION_LEFT)
158+
{
159+
ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH",
160+
(unsigned) options->paddingTop + 1,
161+
(unsigned) options->paddingLeft + 1
162+
);
163+
}
164+
else if (options->position == FF_LOGO_POSITION_TOP)
165+
{
166+
if (!options->width)
167+
{
168+
ffStrbufAppendNC(&buf, options->paddingTop, '\n');
169+
ffStrbufAppendNC(&buf, options->paddingLeft, ' ');
170+
}
171+
else
172+
{
173+
if (printError)
174+
fputs("Logo (kitty-icat): position top is not supported when logo width is set\n", stderr);
175+
return false;
176+
}
177+
}
178+
else if (options->position == FF_LOGO_POSITION_RIGHT)
179+
{
180+
if (printError)
181+
fputs("Logo (kitty-icat): position right is not supported\n", stderr);
182+
return false;
183+
}
184+
185+
uint32_t prevLength = buf.length;
186+
187+
const char* error = NULL;
188+
189+
if (options->width)
190+
{
191+
char place[64];
192+
snprintf(place,
193+
ARRAY_SIZE(place),
194+
"--place=%ux9999@%ux%u",
195+
options->width,
196+
options->paddingLeft + 1,
197+
options->paddingTop + 1);
198+
199+
error = ffProcessAppendStdOut(&buf, (char* []) {
200+
"kitten",
201+
"icat",
202+
"-n",
203+
"--align=left",
204+
place,
205+
"--scale-up",
206+
options->source.chars,
207+
NULL,
208+
});
209+
}
210+
else
211+
{
212+
error = ffProcessAppendStdOut(&buf, (char* []) {
213+
"kitten",
214+
"icat",
215+
"-n",
216+
"--align=left",
217+
options->source.chars,
218+
NULL,
219+
});
220+
}
221+
if (error)
222+
{
223+
if (printError)
224+
fprintf(stderr, "Logo (kitty-icat): running `kitten icat` failed %s\n", error);
225+
return false;
226+
}
227+
228+
if (buf.length == prevLength)
229+
{
230+
if (printError)
231+
fputs("Logo (kitty-icat): `kitten icat` returned empty output\n", stderr);
232+
return false;
233+
}
234+
235+
ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf);
236+
237+
if (options->position == FF_LOGO_POSITION_LEFT || options->position == FF_LOGO_POSITION_RIGHT)
238+
{
239+
uint16_t X = 0, Y = 0;
240+
const char* error = ffGetTerminalResponse("\e[6n", 2, "%*[^0-9]%hu;%huR", &Y, &X);
241+
if (error)
242+
{
243+
fprintf(stderr, "\nLogo (kitty-icat): fail to query cursor position: %s\n", error);
244+
return true; // We already printed image logo, don't print ascii logo then
245+
}
246+
if (X < options->paddingLeft + options->width)
247+
X = (uint16_t) (options->paddingLeft + options->width);
248+
if (options->position == FF_LOGO_POSITION_LEFT)
249+
instance.state.logoWidth = X + options->paddingRight - 1;
250+
instance.state.logoHeight = Y;
251+
fputs("\e[H", stdout);
252+
}
253+
else if (options->position == FF_LOGO_POSITION_TOP)
254+
{
255+
instance.state.logoWidth = instance.state.logoHeight = 0;
256+
ffPrintCharTimes('\n', options->paddingRight);
257+
}
258+
259+
return true;
260+
}
261+
141262
static bool printImageKittyDirect(bool printError)
142263
{
143264
const FFOptionsLogo* options = &instance.config.logo;
@@ -957,6 +1078,9 @@ bool ffLogoPrintImageIfExists(FFLogoType type, bool printError)
9571078
if(type == FF_LOGO_TYPE_IMAGE_KITTY_DIRECT)
9581079
return printImageKittyDirect(printError);
9591080

1081+
if(type == FF_LOGO_TYPE_IMAGE_KITTY_ICAT)
1082+
return printImageKittyIcat(printError);
1083+
9601084
#if !defined(FF_HAVE_CHAFA)
9611085
if(type == FF_LOGO_TYPE_IMAGE_CHAFA)
9621086
{

src/options/logo.c

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ bool ffOptionsParseLogoCommandLine(FFOptionsLogo* options, const char* key, cons
6363
{ "sixel", FF_LOGO_TYPE_IMAGE_SIXEL },
6464
{ "kitty", FF_LOGO_TYPE_IMAGE_KITTY },
6565
{ "kitty-direct", FF_LOGO_TYPE_IMAGE_KITTY_DIRECT },
66+
{ "kitty-icat", FF_LOGO_TYPE_IMAGE_KITTY_ICAT },
6667
{ "iterm", FF_LOGO_TYPE_IMAGE_ITERM },
6768
{ "chafa", FF_LOGO_TYPE_IMAGE_CHAFA },
6869
{ "raw", FF_LOGO_TYPE_IMAGE_RAW },
@@ -283,6 +284,7 @@ const char* ffOptionsParseLogoJsonConfig(FFOptionsLogo* options, yyjson_val* roo
283284
{ "sixel", FF_LOGO_TYPE_IMAGE_SIXEL },
284285
{ "kitty", FF_LOGO_TYPE_IMAGE_KITTY },
285286
{ "kitty-direct", FF_LOGO_TYPE_IMAGE_KITTY_DIRECT },
287+
{ "kitty-icat", FF_LOGO_TYPE_IMAGE_KITTY_ICAT },
286288
{ "iterm", FF_LOGO_TYPE_IMAGE_ITERM },
287289
{ "chafa", FF_LOGO_TYPE_IMAGE_CHAFA },
288290
{ "raw", FF_LOGO_TYPE_IMAGE_RAW },

src/options/logo.h

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ typedef enum __attribute__((__packed__)) FFLogoType
1717
FF_LOGO_TYPE_IMAGE_SIXEL, //image file, printed as sixel codes
1818
FF_LOGO_TYPE_IMAGE_KITTY, //image file, printed as kitty graphics protocol
1919
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)
20+
FF_LOGO_TYPE_IMAGE_KITTY_ICAT, //image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed"
2021
FF_LOGO_TYPE_IMAGE_ITERM, //image file, printed as iterm graphics protocol
2122
FF_LOGO_TYPE_IMAGE_CHAFA, //image file, printed as ascii art using libchafa
2223
FF_LOGO_TYPE_IMAGE_RAW, //image file, printed as raw binary string

0 commit comments

Comments
 (0)