From 295e119810ba4afe0114e0e089e4ecd3ad80541e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 2 Feb 2024 15:09:54 +0800 Subject: [PATCH] Camera (macOS): new module --- CMakeLists.txt | 8 ++ src/common/modules.c | 1 + src/detection/camera/camera.h | 14 +++ src/detection/camera/camera_apple.m | 24 ++++ src/detection/camera/camera_nosupport.c | 6 + src/detection/camera/camera_windows.c | 6 + src/modules/camera/camera.c | 158 ++++++++++++++++++++++++ src/modules/camera/camera.h | 9 ++ src/modules/camera/option.h | 11 ++ src/modules/modules.h | 1 + src/modules/options.h | 1 + src/options/modules.c | 2 + src/options/modules.h | 1 + 13 files changed, 242 insertions(+) create mode 100644 src/detection/camera/camera.h create mode 100644 src/detection/camera/camera_apple.m create mode 100644 src/detection/camera/camera_nosupport.c create mode 100644 src/detection/camera/camera_windows.c create mode 100644 src/modules/camera/camera.c create mode 100644 src/modules/camera/camera.h create mode 100644 src/modules/camera/option.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e4676a3f6..65c8fac78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,6 +308,7 @@ set(LIBFASTFETCH_SRC src/modules/board/board.c src/modules/brightness/brightness.c src/modules/break/break.c + src/modules/camera/camera.c src/modules/chassis/chassis.c src/modules/colors/colors.c src/modules/cpu/cpu.c @@ -432,6 +433,7 @@ if(LINUX) src/detection/wm/wm_nosupport.c src/detection/de/de_linux.c src/detection/wmtheme/wmtheme_linux.c + src/detection/camera/camera_nosupport.c src/util/platform/FFPlatform_unix.c ) elseif(ANDROID) @@ -485,6 +487,7 @@ elseif(ANDROID) src/detection/wm/wm_nosupport.c src/detection/de/de_nosupport.c src/detection/wmtheme/wmtheme_nosupport.c + src/detection/camera/camera_nosupport.c src/util/platform/FFPlatform_unix.c ) elseif(BSD) @@ -547,6 +550,7 @@ elseif(BSD) src/detection/wm/wm_nosupport.c src/detection/de/de_linux.c src/detection/wmtheme/wmtheme_linux.c + src/detection/camera/camera_nosupport.c src/util/platform/FFPlatform_unix.c ) elseif(APPLE) @@ -602,6 +606,7 @@ elseif(APPLE) src/detection/wm/wm_apple.c src/detection/de/de_nosupport.c src/detection/wmtheme/wmtheme_apple.m + src/detection/camera/camera_apple.m src/util/apple/cf_helpers.c src/util/apple/osascript.m src/util/platform/FFPlatform_unix.c @@ -657,6 +662,7 @@ elseif(WIN32) src/detection/wm/wm_windows.cpp src/detection/de/de_nosupport.c src/detection/wmtheme/wmtheme_windows.c + src/detection/camera/camera_windows.c src/util/windows/getline.c src/util/windows/com.cpp src/util/windows/registry.c @@ -912,9 +918,11 @@ if(LINUX) ) elseif(APPLE) target_link_libraries(libfastfetch + PRIVATE "-framework AVFoundation" PRIVATE "-framework Cocoa" PRIVATE "-framework CoreFoundation" PRIVATE "-framework CoreAudio" + PRIVATE "-framework CoreMedia" PRIVATE "-framework CoreVideo" PRIVATE "-framework CoreWLAN" PRIVATE "-framework IOBluetooth" diff --git a/src/common/modules.c b/src/common/modules.c index fa486261f..718a8114a 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -15,6 +15,7 @@ static FFModuleBaseInfo* B[] = { }; static FFModuleBaseInfo* C[] = { + (void*) &instance.config.modules.camera, (void*) &instance.config.modules.chassis, (void*) &instance.config.modules.command, (void*) &instance.config.modules.colors, diff --git a/src/detection/camera/camera.h b/src/detection/camera/camera.h new file mode 100644 index 000000000..d55573d31 --- /dev/null +++ b/src/detection/camera/camera.h @@ -0,0 +1,14 @@ +#pragma once + +#include "fastfetch.h" + +typedef struct FFCameraResult +{ + FFstrbuf name; + FFstrbuf vendor; + FFstrbuf id; + uint32_t width; + uint32_t height; +} FFCameraResult; + +const char* ffDetectCamera(FFlist* result /* list of FFCameraResult */); diff --git a/src/detection/camera/camera_apple.m b/src/detection/camera/camera_apple.m new file mode 100644 index 000000000..e3cd5b0c9 --- /dev/null +++ b/src/detection/camera/camera_apple.m @@ -0,0 +1,24 @@ +#include "camera.h" + +#import + +const char* ffDetectCamera(FFlist* result) +{ + AVCaptureDeviceDiscoverySession* session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; + if (!session) + return "Failed to create AVCaptureDeviceDiscoverySession"; + + for (AVCaptureDevice* device in session.devices) + { + FFCameraResult* camera = (FFCameraResult*) ffListAdd(result); + ffStrbufInitS(&camera->name, device.localizedName.UTF8String); + ffStrbufInitS(&camera->vendor, device.manufacturer.UTF8String); + ffStrbufInitS(&camera->id, device.uniqueID.UTF8String); + + CMVideoDimensions size = CMVideoFormatDescriptionGetDimensions(device.activeFormat.formatDescription); + camera->width = size.width < 0 ? 0 : (uint32_t) size.width; + camera->height = size.height < 0 ? 0 : (uint32_t) size.height; + } + + return NULL; +} diff --git a/src/detection/camera/camera_nosupport.c b/src/detection/camera/camera_nosupport.c new file mode 100644 index 000000000..ff56815f2 --- /dev/null +++ b/src/detection/camera/camera_nosupport.c @@ -0,0 +1,6 @@ +#include "camera.h" + +const char* ffDetectCamera(FF_MAYBE_UNUSED FFlist* result) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/camera/camera_windows.c b/src/detection/camera/camera_windows.c new file mode 100644 index 000000000..ff56815f2 --- /dev/null +++ b/src/detection/camera/camera_windows.c @@ -0,0 +1,6 @@ +#include "camera.h" + +const char* ffDetectCamera(FF_MAYBE_UNUSED FFlist* result) +{ + return "Not supported on this platform"; +} diff --git a/src/modules/camera/camera.c b/src/modules/camera/camera.c new file mode 100644 index 000000000..35cc99180 --- /dev/null +++ b/src/modules/camera/camera.c @@ -0,0 +1,158 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "detection/libc/libc.h" +#include "detection/camera/camera.h" +#include "modules/camera/camera.h" +#include "util/stringUtils.h" + +#define FF_CAMERA_NUM_FORMAT_ARGS 5 + +static void printDevice(FFCameraOptions* options, const FFCameraResult* device, uint8_t index) +{ + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(FF_CAMERA_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + if (device->width > 0 && device->height > 0) + printf("%s (%upx x %upx)\n", device->name.chars, (unsigned) device->width, (unsigned) device->height); + else + ffStrbufPutTo(&device->name, stdout); + } + else + { + ffPrintFormat(FF_CAMERA_MODULE_NAME, index, &options->moduleArgs, FF_CAMERA_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &device->name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->vendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->id}, + {FF_FORMAT_ARG_TYPE_UINT, &device->width}, + {FF_FORMAT_ARG_TYPE_UINT, &device->height} + }); + } +} + +void ffPrintCamera(FFCameraOptions* options) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFCameraResult)); + const char* error = ffDetectCamera(&result); + + if (error) + { + ffPrintError(FF_CAMERA_MODULE_NAME, 0, &options->moduleArgs, "%s", error); + return; + } + + if (result.length == 0) + { + ffPrintError(FF_CAMERA_MODULE_NAME, 0, &options->moduleArgs, "No camera found"); + return; + } + + for(uint32_t i = 0; i < result.length; i++) + { + uint8_t index = (uint8_t) (result.length == 1 ? 0 : i + 1); + printDevice(options, FF_LIST_GET(FFCameraResult, result, i), index); + } + + FF_LIST_FOR_EACH(FFCameraResult, dev, result) + { + ffStrbufDestroy(&dev->name); + ffStrbufDestroy(&dev->id); + } +} + +bool ffParseCameraCommandOptions(FFCameraOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_CAMERA_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + return false; +} + +void ffParseCameraJsonObject(FFCameraOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + ffPrintError(FF_CAMERA_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); + } +} + +void ffGenerateCameraJsonConfig(FFCameraOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyCameraOptions))) FFCameraOptions defaultOptions; + ffInitCameraOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); +} + +void ffGenerateCameraJsonResult(FF_MAYBE_UNUSED FFCameraOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFCameraResult)); + const char* error = ffDetectCamera(&result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFCameraResult, dev, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &dev->name); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &dev->vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "id", &dev->id); + yyjson_mut_obj_add_uint(doc, obj, "width", dev->width); + yyjson_mut_obj_add_uint(doc, obj, "height", dev->height); + } + + FF_LIST_FOR_EACH(FFCameraResult, dev, result) + { + ffStrbufDestroy(&dev->name); + ffStrbufDestroy(&dev->id); + } +} + +void ffPrintCameraHelpFormat(void) +{ + ffPrintModuleFormatHelp(FF_CAMERA_MODULE_NAME, "{1} ({4}px x {5}px)", FF_CAMERA_NUM_FORMAT_ARGS, (const char* []) { + "Device name", + "Vendor", + "Identifier", + "Width (in px)", + "Height (in px)", + }); +} + +void ffInitCameraOptions(FFCameraOptions* options) +{ + ffOptionInitModuleBaseInfo( + &options->moduleInfo, + FF_CAMERA_MODULE_NAME, + "Print available cameras", + ffParseCameraCommandOptions, + ffParseCameraJsonObject, + ffPrintCamera, + ffGenerateCameraJsonResult, + ffPrintCameraHelpFormat, + ffGenerateCameraJsonConfig + ); + ffOptionInitModuleArg(&options->moduleArgs); +} + +void ffDestroyCameraOptions(FFCameraOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/camera/camera.h b/src/modules/camera/camera.h new file mode 100644 index 000000000..47d2e1cc7 --- /dev/null +++ b/src/modules/camera/camera.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_CAMERA_MODULE_NAME "Camera" + +void ffPrintCamera(FFCameraOptions* options); +void ffInitCameraOptions(FFCameraOptions* options); +void ffDestroyCameraOptions(FFCameraOptions* options); diff --git a/src/modules/camera/option.h b/src/modules/camera/option.h new file mode 100644 index 000000000..7c7ac05b6 --- /dev/null +++ b/src/modules/camera/option.h @@ -0,0 +1,11 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" + +typedef struct FFCameraOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; +} FFCameraOptions; diff --git a/src/modules/modules.h b/src/modules/modules.h index 78fe2f33b..84c7f9dec 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -8,6 +8,7 @@ #include "modules/brightness/brightness.h" #include "modules/board/board.h" #include "modules/break/break.h" +#include "modules/camera/camera.h" #include "modules/chassis/chassis.h" #include "modules/cpu/cpu.h" #include "modules/cpuusage/cpuusage.h" diff --git a/src/modules/options.h b/src/modules/options.h index 54dd7ca15..1e998396a 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -8,6 +8,7 @@ #include "modules/board/option.h" #include "modules/break/option.h" #include "modules/brightness/option.h" +#include "modules/camera/option.h" #include "modules/chassis/option.h" #include "modules/cpu/option.h" #include "modules/cpuusage/option.h" diff --git a/src/options/modules.c b/src/options/modules.c index bc636ff0c..0de31e727 100644 --- a/src/options/modules.c +++ b/src/options/modules.c @@ -9,6 +9,7 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitBoardOptions(&options->board); ffInitBreakOptions(&options->break_); ffInitBrightnessOptions(&options->brightness); + ffInitCameraOptions(&options->camera); ffInitCPUOptions(&options->cpu); ffInitCPUUsageOptions(&options->cpuUsage); ffInitChassisOptions(&options->chassis); @@ -72,6 +73,7 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyBoardOptions(&options->board); ffDestroyBreakOptions(&options->break_); ffDestroyBrightnessOptions(&options->brightness); + ffDestroyCameraOptions(&options->camera); ffDestroyCPUOptions(&options->cpu); ffDestroyCPUUsageOptions(&options->cpuUsage); ffDestroyChassisOptions(&options->chassis); diff --git a/src/options/modules.h b/src/options/modules.h index 7a96a0f40..7c19db986 100644 --- a/src/options/modules.h +++ b/src/options/modules.h @@ -12,6 +12,7 @@ typedef struct FFOptionsModules FFBrightnessOptions brightness; FFCPUOptions cpu; FFCPUUsageOptions cpuUsage; + FFCameraOptions camera; FFChassisOptions chassis; FFColorsOptions colors; FFCommandOptions command;