diff --git a/CMakeLists.txt b/CMakeLists.txt index efb64b13b..ce8339acf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ OPTION(ENABLE_RPM "Enable rpm" ON) OPTION(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON) OPTION(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON) OPTION(ENABLE_ZLIB "Enable zlib" ON) +OPTION(ENABLE_CHAFA "Enable chafa" ON) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) @@ -321,6 +322,15 @@ if(ENABLE_ZLIB) endif() endif() +if(ENABLE_CHAFA) + pkg_check_modules(CHAFA chafa) + if(CHAFA_FOUND) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_CHAFA=1) + else() + message(WARNING "Package chafa not found. Building without support.") + endif() +endif() + target_include_directories(libfastfetch PUBLIC ${PROJECT_BINARY_DIR} PUBLIC ${PROJECT_SOURCE_DIR}/src @@ -340,6 +350,7 @@ target_include_directories(libfastfetch PRIVATE ${IMAGEMAGICK7_INCLUDE_DIRS} PRIVATE ${IMAGEMAGICK6_INCLUDE_DIRS} PRIVATE ${ZLIB_INCLUDE_DIRS} + PRIVATE ${CHAFA_INCLUDE_DIRS} ) target_link_libraries(libfastfetch diff --git a/README.md b/README.md index 37c386d1b..294c58754 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ The following libraries are used if present at runtime: * [`libGIO`](https://developer.gnome.org/gio/unstable/): Needed for values that are only stored GSettings. * [`libDConf`](https://developer.gnome.org/dconf/unstable/): Needed for values that are only stored in DConf + Fallback for GSettings. * [`libmagickcore` (ImageMagick)](https://www.imagemagick.org/): Images in terminal using sixel or kitty graphics protocol. +* ['libchafa`](https://github.com/hpjansson/chafa): Image output as ascii art. * [`libZ`](https://www.zlib.net/): Faster image output when using kitty graphics protocol. * [`libDBus`](https://www.freedesktop.org/wiki/Software/dbus): Needed for detecting current media player and song. * [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font. diff --git a/completions/bash b/completions/bash index babf52096..c50cd2f66 100644 --- a/completions/bash +++ b/completions/bash @@ -234,6 +234,7 @@ __fastfetch_completion() "--lib-RPM" "--lib-imagemagick" "--lib-z" + "--lib-chafa" "--battery-dir" "--load-config" ) diff --git a/src/common/init.c b/src/common/init.c index 38f66352f..c38f05e90 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -201,6 +201,7 @@ static void defaultConfig(FFinstance* instance) ffStrbufInitA(&instance->config.librpm, 0); ffStrbufInitA(&instance->config.libImageMagick, 0); ffStrbufInitA(&instance->config.libZ, 0); + ffStrbufInitA(&instance->config.libChafa, 0); ffStrbufInitA(&instance->config.diskFolders, 0); diff --git a/src/data/config.txt b/src/data/config.txt index c3310a60f..565e389c9 100644 --- a/src/data/config.txt +++ b/src/data/config.txt @@ -61,7 +61,7 @@ # Logo type option: # Sets the logo type to use. -# Must be auto, builtin, file, raw, sixel or kitty. +# Must be auto, builtin, file, raw, sixel, kitty or chafa. # Default is auto. #--logo-type auto @@ -230,3 +230,4 @@ #--lib-rpm /usr/lib/librpm.so #--lib-imagemagick /usr/lib/libMagickCore-7.Q16HDRI.so #--lib-z /usr/lib/libz.so +#--lib-chafa /usr/lib/libchafa.so diff --git a/src/data/help.txt b/src/data/help.txt index dcc67214f..4c9dccb45 100644 --- a/src/data/help.txt +++ b/src/data/help.txt @@ -21,7 +21,7 @@ General options: Logo options: -l,--logo : set the logo to use. The type is specified by --logo-type. If default: the name of a builtin logo or a path to a file - --logo-type : set the type of the logo given. Must be auto, builtin, file, raw, sixel or kitty. + --logo-type : set the type of the logo given. Must be auto, builtin, file, raw, sixel, kitty or chafa. --logo-width : set the width of the logo (in characters), if it is an image --logo-color-[1-9] : overwrite a color in the logo --logo-padding : set the padding on the left and the right of the logo @@ -30,6 +30,7 @@ Logo options: --logo-print-remaining : weather to print the remaining logo, if it has more lines than modules to display --sixel : short for --logo-type sixel --logo --kitty : short for --logo-type kitty --logo + --chafa : short for --logo-type chafa --logo --file : short for --logo-type file --logo --raw : short for --logo-type raw --logo @@ -123,6 +124,7 @@ Library options: Set the path of a library to load --lib-RPM --lib-imagemagick --lib-z + --lib-chafa Module specific options: --separator-string : Set the string printed by the separator module diff --git a/src/fastfetch.c b/src/fastfetch.c index b3960688d..ea9c852b5 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -735,6 +735,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con instance->config.logoType = FF_LOGO_TYPE_SIXEL; else if(strcasecmp(value, "kitty") == 0) instance->config.logoType = FF_LOGO_TYPE_KITTY; + else if(strcasecmp(value, "chafa") == 0) + instance->config.logoType = FF_LOGO_TYPE_CHAFA; else { fprintf(stderr, "Error: unknown logo type: %s\n", value); @@ -789,6 +791,11 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con optionParseString(key, value, &instance->config.logoSource); instance->config.logoType = FF_LOGO_TYPE_RAW; } + else if(strcasecmp(key, "--chafa") == 0) + { + optionParseString(key, value, &instance->config.logoSource); + instance->config.logoType = FF_LOGO_TYPE_CHAFA; + } /////////////////// //Display options// @@ -988,6 +995,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con optionParseString(key, value, &instance->config.libImageMagick); else if(strcasecmp(key, "--lib-z") == 0) optionParseString(key, value, &instance->config.libZ); + else if(strcasecmp(key, "--lib-chafa") == 0) + optionParseString(key, value, &instance->config.libChafa); ////////////////// //Module options// diff --git a/src/fastfetch.h b/src/fastfetch.h index f6ad66088..83d7280a2 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -38,7 +38,8 @@ typedef enum FFLogoType FF_LOGO_TYPE_FILE, //Raw text file, printed as is. FF_LOGO_TYPE_RAW, //Raw text file, printed with color codes replacement. FF_LOGO_TYPE_SIXEL, //Image file, printed as sixel codes. - FF_LOGO_TYPE_KITTY //Image file, printed as kitty graphics protocol + FF_LOGO_TYPE_KITTY, //Image file, printed as kitty graphics protocol + FF_LOGO_TYPE_CHAFA //Image file, printed as ascii art using libchafa } FFLogoType; typedef struct FFconfig @@ -138,6 +139,7 @@ typedef struct FFconfig FFstrbuf librpm; FFstrbuf libImageMagick; FFstrbuf libZ; + FFstrbuf libChafa; FFstrbuf diskFolders; diff --git a/src/logo/image/image.c b/src/logo/image/image.c index 23fdff2ea..5be9d480e 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -8,6 +8,7 @@ #define FF_CACHE_FILE_SIXEL "sixel" #define FF_CACHE_FILE_KITTY_COMPRESSED "kittyc" #define FF_CACHE_FILE_KITTY_UNCOMPRESSED "kittyu" +#define FF_CACHE_FILE_CHAFA "chafa" #include #include @@ -88,6 +89,10 @@ static void finishPrinting(FFinstance* instance, const FFLogoRequestData* reques if(requestData->type == FF_LOGO_TYPE_SIXEL) instance->state.logoHeight += 1; + //Chafa ascii images don't have a newline at the end + if(requestData->type == FF_LOGO_TYPE_CHAFA) + instance->state.logoHeight -= 1; + fputs("\033[9999999D", stdout); printf("\033[%uA", instance->state.logoHeight); } @@ -193,6 +198,66 @@ static bool printImageKitty(FFinstance* instance, FFLogoRequestData* requestData return true; } +#ifdef FF_HAVE_CHAFA +#include +static bool printImageChafa(FFinstance* instance, FFLogoRequestData* requestData, const ImageData* imageData) +{ + FF_LIBRARY_LOAD(chafa, instance->config.libChafa, false, "libchafa.so", 1) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_new, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_add_by_tags, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_new, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_geometry, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_set_symbol_map, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_new, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_draw_all_pixels, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_print, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_unref, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_canvas_config_unref, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, chafa_symbol_map_unref, false) + FF_LIBRARY_LOAD_SYMBOL(chafa, g_string_free, false) + + imageData->ffCopyMagickString(imageData->imageInfo->magick, "RGBA", 5); + size_t length; + void* blob = imageData->ffImageToBlob(imageData->imageInfo, imageData->image, &length, imageData->exceptionInfo); + if(!checkAllocationResult(blob, length)) + return false; + + ChafaSymbolMap* symbolMap = ffchafa_symbol_map_new(); + ffchafa_symbol_map_add_by_tags(symbolMap, CHAFA_SYMBOL_TAG_ALL); + + //imagePixelHeight is calculated, as if the image was already resized + gint logoCharacterHeight = (gint) (requestData->imagePixelHeight / requestData->characterPixelHeight); + + ChafaCanvasConfig* canvasConfig = ffchafa_canvas_config_new(); + ffchafa_canvas_config_set_geometry(canvasConfig, (gint) instance->config.logoWidth, logoCharacterHeight); + ffchafa_canvas_config_set_symbol_map(canvasConfig, symbolMap); + + ChafaCanvas* canvas = ffchafa_canvas_new(canvasConfig); + ffchafa_canvas_draw_all_pixels( + canvas, + CHAFA_PIXEL_RGBA8_UNASSOCIATED, + blob, + (gint) imageData->image->columns, + (gint) imageData->image->rows, + (gint) imageData->image->columns * 4 + ); + + GString* str = ffchafa_canvas_print(canvas, NULL); + + FFstrbuf result; + result.chars = str->str; + result.length = (uint32_t) str->len; + writeResult(instance, requestData, &result, FF_CACHE_FILE_CHAFA); + + ffg_string_free(str, TRUE); + ffchafa_canvas_unref(canvas); + ffchafa_canvas_config_unref(canvasConfig); + ffchafa_symbol_map_unref(symbolMap); + + return true; +} +#endif + FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* requestData, const FFIMData* imData) { FF_LIBRARY_LOAD_SYMBOL(imData->library, AcquireExceptionInfo, FF_LOGO_IMAGE_RESULT_INIT_ERROR) @@ -239,12 +304,20 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* return FF_LOGO_IMAGE_RESULT_RUN_ERROR; } - imageData.image = (Image*) imData->resizeFunc(originalImage, requestData->imagePixelWidth, requestData->imagePixelHeight, imageData.exceptionInfo); - ffDestroyImage(originalImage); - if(imageData.image == NULL) + if(instance->config.logoType != FF_LOGO_TYPE_CHAFA) { - ffDestroyExceptionInfo(imageData.exceptionInfo); - return FF_LOGO_IMAGE_RESULT_RUN_ERROR; + imageData.image = (Image*) imData->resizeFunc(originalImage, requestData->imagePixelWidth, requestData->imagePixelHeight, imageData.exceptionInfo); + ffDestroyImage(originalImage); + if(imageData.image == NULL) + { + ffDestroyExceptionInfo(imageData.exceptionInfo); + return FF_LOGO_IMAGE_RESULT_RUN_ERROR; + } + } + else + { + //We don't want to resize the image for chafa, because chafa does that while converting to ASCII. + imageData.image = originalImage; } imageData.imageInfo = ffAcquireImageInfo(); @@ -256,10 +329,18 @@ FFLogoImageResult ffLogoPrintImageImpl(FFinstance* instance, FFLogoRequestData* } bool printSuccessful; - if(requestData->type == FF_LOGO_TYPE_KITTY) - printSuccessful = printImageKitty(instance, requestData, &imageData); - else + if(requestData->type == FF_LOGO_TYPE_SIXEL) printSuccessful = printImageSixel(instance, requestData, &imageData); + else if(requestData->type == FF_LOGO_TYPE_KITTY) + printSuccessful = printImageKitty(instance, requestData, &imageData); + else if(requestData->type == FF_LOGO_TYPE_CHAFA) + #ifdef FF_HAVE_CHAFA + printSuccessful = printImageChafa(instance, requestData, &imageData); + #else //should never happen at this point + printSuccessful = false; + #endif + else //should never happen at this point + printSuccessful = false; ffDestroyImageInfo(imageData.imageInfo); ffDestroyImage(imageData.image); @@ -302,8 +383,12 @@ static bool printCached(FFinstance* instance, FFLogoRequestData* requestData) if(fd == -1) fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_UNCOMPRESSED); } - else + else if(requestData->type == FF_LOGO_TYPE_SIXEL) fd = getCacheFD(requestData, FF_CACHE_FILE_SIXEL); + else if(requestData->type == FF_LOGO_TYPE_CHAFA) + fd = getCacheFD(requestData, FF_CACHE_FILE_CHAFA); + else + fd = -1; //should never happen at this point if(fd == -1) return false; @@ -344,6 +429,12 @@ static bool getTermInfo(struct winsize* winsize) bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type) { + //Performance optimization + #ifndef FF_HAVE_CHAFA + if(type == FF_LOGO_TYPE_CHAFA) + return false; + #endif + FFLogoRequestData requestData; if(!getTermInfo(&requestData.winsize)) diff --git a/src/logo/logo.c b/src/logo/logo.c index 867b02ad2..f540e3376 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -162,7 +162,8 @@ static void logoPrintDetected(FFinstance* instance) { if( !ffLogoPrintBuiltinIfExists(instance) && - !ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_KITTY) + !ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_KITTY) && + !ffLogoPrintImageIfExists(instance, FF_LOGO_TYPE_CHAFA) ) logoPrintFile(instance, true); return; } @@ -179,7 +180,9 @@ void ffPrintLogo(FFinstance* instance) instance->config.logoType == FF_LOGO_TYPE_BUILTIN || instance->config.logoType == FF_LOGO_TYPE_FILE || instance->config.logoType == FF_LOGO_TYPE_RAW || - instance->config.logoType == FF_LOGO_TYPE_SIXEL + instance->config.logoType == FF_LOGO_TYPE_SIXEL || + instance->config.logoType == FF_LOGO_TYPE_KITTY || + instance->config.logoType == FF_LOGO_TYPE_CHAFA ) && instance->config.logoSource.length == 0) ffLogoPrintUnknown(instance); else if(instance->config.logoType == FF_LOGO_TYPE_BUILTIN) @@ -191,7 +194,7 @@ void ffPrintLogo(FFinstance* instance) logoPrintFile(instance, true); else if(instance->config.logoType == FF_LOGO_TYPE_RAW) logoPrintFile(instance, false); - else if(instance->config.logoType == FF_LOGO_TYPE_SIXEL || instance->config.logoType == FF_LOGO_TYPE_KITTY) + else if(instance->config.logoType == FF_LOGO_TYPE_SIXEL || instance->config.logoType == FF_LOGO_TYPE_KITTY || instance->config.logoType == FF_LOGO_TYPE_CHAFA) { if(!ffLogoPrintImageIfExists(instance, instance->config.logoType)) ffLogoPrintBuiltinDetected(instance); diff --git a/src/logo/logo.h b/src/logo/logo.h index 96853e2c9..cb7698e23 100644 --- a/src/logo/logo.h +++ b/src/logo/logo.h @@ -15,7 +15,7 @@ void ffLogoPrintUnknown(FFinstance* instance); bool ffLogoPrintBuiltinIfExists(FFinstance* instance); void ffLogoPrintBuiltinDetected(FFinstance* instance); -//sixel/image.c +//image/image.c bool ffLogoPrintImageIfExists(FFinstance* instance, FFLogoType type); #endif