mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-11-08 22:20:17 +01:00
Global: fixes issues found by AI
This commit is contained in:
@@ -172,23 +172,24 @@ static const char* parseBinder(FFlist* results) {
|
||||
return error;
|
||||
}
|
||||
|
||||
uint32_t handle = 0;
|
||||
error = ffBinderLookupService(&binder, FF_BATTERY_ANDROID_SERVICE, FF_BINDER_SM_GET_SERVICE, &handle);
|
||||
// Released on every path out of this function, including the ones below that return early.
|
||||
[[gnu::cleanup(ffBinderServiceHandleRelease)]] FFBinderServiceHandle service = { .binder = &binder };
|
||||
error = ffBinderLookupService(&binder, FF_BATTERY_ANDROID_SERVICE, FF_BINDER_SM_GET_SERVICE, &service.handle);
|
||||
if (error != nullptr) {
|
||||
FF_DEBUG("Looking up the \"%s\" service failed: %s", FF_BATTERY_ANDROID_SERVICE, error);
|
||||
return error;
|
||||
}
|
||||
FF_DEBUG("The \"%s\" service is handle %u", FF_BATTERY_ANDROID_SERVICE, handle);
|
||||
FF_DEBUG("The \"%s\" service is handle %u", FF_BATTERY_ANDROID_SERVICE, service.handle);
|
||||
|
||||
uint64_t capacity = 0;
|
||||
error = getProperty(&binder, handle, FF_BATTERY_ANDROID_PROPERTY_CAPACITY, &capacity);
|
||||
error = getProperty(&binder, service.handle, FF_BATTERY_ANDROID_PROPERTY_CAPACITY, &capacity);
|
||||
if (error != nullptr) {
|
||||
FF_DEBUG("The capacity is what failed, so no battery is reported at all");
|
||||
return error;
|
||||
}
|
||||
|
||||
uint64_t status = 0;
|
||||
error = getProperty(&binder, handle, FF_BATTERY_ANDROID_PROPERTY_STATUS, &status);
|
||||
error = getProperty(&binder, service.handle, FF_BATTERY_ANDROID_PROPERTY_STATUS, &status);
|
||||
if (error != nullptr) {
|
||||
FF_DEBUG("The status is what failed, so no battery is reported at all");
|
||||
return error;
|
||||
|
||||
@@ -271,6 +271,13 @@ static void setClassOfDevice(FFstrbuf* type, IOBluetoothDevice* ioDevice) {
|
||||
static FFBluetoothResult* findByName(FFlist* devices, const char* name) {
|
||||
FFBluetoothResult* found = nullptr;
|
||||
FF_LIST_FOR_EACH (FFBluetoothResult, device, *devices) {
|
||||
// Only the classic half counts. The LE pass runs after the classic one and appends its
|
||||
// entries to the same list, so the peripheral an earlier iteration of this very loop created
|
||||
// would otherwise make its own name look ambiguous -- and the rule is there to keep one name
|
||||
// from standing for two classic devices, not to count the LE copy of one of them.
|
||||
if (!(device->deviceType & FF_BLUETOOTH_DEVICE_TYPE_CLASSIC_BIT)) {
|
||||
continue;
|
||||
}
|
||||
if (!ffStrEqualsIgnCase(device->name.chars, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,14 @@ static bool readHidReport(HANDLE hHidFile, uint8_t* buffer, uint32_t length, DWO
|
||||
*nBytes = read;
|
||||
return true;
|
||||
}
|
||||
CancelIo(hHidFile);
|
||||
|
||||
// A timeout leaves the read pending, and the driver writes into `buffer` and `overlapped` when
|
||||
// it eventually completes -- both of which are gone by then: the buffer belongs to the caller
|
||||
// and the OVERLAPPED sits on this frame. So the read is cancelled by its own OVERLAPPED rather
|
||||
// than by handle (CancelIo would take every other read on the same handle with it), and the
|
||||
// cancellation is waited for before returning.
|
||||
CancelIoEx(hHidFile, &overlapped);
|
||||
GetOverlappedResult(hHidFile, &overlapped, &read, TRUE);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -299,6 +306,13 @@ static FFGamepadBatteryKind detectBatteryKind(uint32_t vendorId, uint32_t produc
|
||||
return FF_GAMEPAD_BATTERY_NONE;
|
||||
}
|
||||
|
||||
// The Nintendo list is deliberately shorter than the name table above. The Charging Grip
|
||||
// (0x200E) is a rail that charges Joy-Con and has no battery of its own, and the SNES
|
||||
// online controller (0x2017) and the Switch 2 controllers (0x2066-0x2069) are ones SDL --
|
||||
// which is what this parsing follows -- reports no battery for: `SDL_hidapi_switch2.c` has
|
||||
// no power reporting at all, and the SNES entry is commented out of `controller_list.h`.
|
||||
// Reading the Switch state report from them would be a guess, so they keep the module's
|
||||
// "unknown".
|
||||
case 0x057E: // Nintendo
|
||||
switch (productId) {
|
||||
case 0x2006: // Joy-Con L
|
||||
|
||||
@@ -80,6 +80,10 @@ static void applyWTProfile(yyjson_val* profile, uint8_t priority, FFTerminalFont
|
||||
// A fragment either defines a profile with "guid" or updates an existing one with "updates",
|
||||
// the latter being more important, so it is looked up first.
|
||||
// Note that "guid" and "updates" may be missing: yyjson_get_str() returns nullptr then.
|
||||
// `fromUpdates` may be null: it is only useful to a caller that ranks the two apart, which a
|
||||
// fragment does. settings.json does not -- an "updates" entry and a "guid" entry in profiles.list
|
||||
// both sit at the same point of the inheritance chain, and the two passes above are what pick the
|
||||
// right one between them.
|
||||
static yyjson_val* findWTProfileInArray(yyjson_val* profiles, const FFstrbuf* wtProfileId, bool* fromUpdates) {
|
||||
if (!yyjson_is_arr(profiles)) {
|
||||
return nullptr;
|
||||
@@ -91,7 +95,9 @@ static yyjson_val* findWTProfileInArray(yyjson_val* profiles, const FFstrbuf* wt
|
||||
yyjson_arr_foreach (profiles, idx, max, profile) {
|
||||
const char* id = yyjson_get_str(yyjson_obj_get(profile, pass == 0 ? "updates" : "guid"));
|
||||
if (id && ffStrbufEqualS(wtProfileId, id)) {
|
||||
*fromUpdates = pass == 0;
|
||||
if (fromUpdates) {
|
||||
*fromUpdates = pass == 0;
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
@@ -122,8 +128,7 @@ static const char* detectFromWTSettings(FFstrbuf* content, const FFstrbuf* wtPro
|
||||
}
|
||||
|
||||
if (wtProfileId->length > 0) {
|
||||
bool fromUpdates = false;
|
||||
yyjson_val* profile = findWTProfileInArray(yyjson_obj_get(profiles, "list"), wtProfileId, &fromUpdates);
|
||||
yyjson_val* profile = findWTProfileInArray(yyjson_obj_get(profiles, "list"), wtProfileId, nullptr);
|
||||
if (profile) {
|
||||
applyWTProfile(profile, WT_FONT_PRIORITY_PROFILE, result);
|
||||
}
|
||||
@@ -167,25 +172,40 @@ static void detectWTProfileFromFragmentsIn(const FFstrbuf* fragmentDir, const FF
|
||||
const uint32_t baseLength = path.length;
|
||||
|
||||
ffStrbufAppendC(&path, '*');
|
||||
WIN32_FIND_DATAA entry;
|
||||
FF_AUTO_CLOSE_DIR HANDLE hFind = FindFirstFileA(path.chars, &entry);
|
||||
// The wide enumeration, because the directory above came from SHGetKnownFolderPath(): a profile
|
||||
// whose name is not ASCII would otherwise hand FindFirstFileA a UTF-8 path that it reads as ANSI,
|
||||
// and no fragment would be found at all.
|
||||
wchar_t pathW[MAX_PATH];
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), nullptr, path.chars, (ULONG) path.length + 1))) {
|
||||
return;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAW entry;
|
||||
FF_AUTO_CLOSE_DIR HANDLE hFind = FindFirstFileW(pathW, &entry);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!(entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || entry.cFileName[0] == '.') {
|
||||
if (!(entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || entry.cFileName[0] == L'.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(&path, baseLength);
|
||||
ffStrbufAppendS(&path, entry.cFileName);
|
||||
ffStrbufAppendWS(&path, entry.cFileName);
|
||||
ffStrbufAppendC(&path, '\\');
|
||||
const uint32_t appLength = path.length;
|
||||
|
||||
ffStrbufAppendS(&path, "*.json");
|
||||
WIN32_FIND_DATAA fileEntry;
|
||||
FF_AUTO_CLOSE_DIR HANDLE hFile = FindFirstFileA(path.chars, &fileEntry);
|
||||
// The path changed, so the pattern has to be converted again. A name that cannot be converted
|
||||
// is skipped rather than failing the whole scan: the next iteration rebuilds `path` from
|
||||
// `baseLength` anyway.
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(pathW, (ULONG) sizeof(pathW), nullptr, path.chars, (ULONG) path.length + 1))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAW fileEntry;
|
||||
FF_AUTO_CLOSE_DIR HANDLE hFile = FindFirstFileW(pathW, &fileEntry);
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (fileEntry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
@@ -193,13 +213,13 @@ static void detectWTProfileFromFragmentsIn(const FFstrbuf* fragmentDir, const FF
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(&path, appLength);
|
||||
ffStrbufAppendS(&path, fileEntry.cFileName);
|
||||
ffStrbufAppendWS(&path, fileEntry.cFileName);
|
||||
applyWTFragmentFile(path.chars, wtProfileId, result);
|
||||
} while (FindNextFileA(hFile, &fileEntry));
|
||||
} while (FindNextFileW(hFile, &fileEntry));
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(&path, baseLength);
|
||||
} while (FindNextFileA(hFind, &entry));
|
||||
} while (FindNextFileW(hFind, &entry));
|
||||
}
|
||||
|
||||
static void detectFromWTFragments(const FFstrbuf* wtProfileId, FFTerminalFontWT* result) {
|
||||
|
||||
+19
-5
@@ -4,6 +4,7 @@
|
||||
|
||||
static FFlist first;
|
||||
static double startTick;
|
||||
static FFTopTypes preparedTypes;
|
||||
|
||||
void ffPrepareTopProcesses(FFTopTypes showTypes) {
|
||||
if ((showTypes & (FF_TOP_TYPE_CPU | FF_TOP_TYPE_DISK)) == 0) {
|
||||
@@ -11,13 +12,26 @@ void ffPrepareTopProcesses(FFTopTypes showTypes) {
|
||||
}
|
||||
|
||||
if (startTick != 0) {
|
||||
return; // Already prepared
|
||||
// Already prepared. The baseline above is one snapshot shared by every `top` module in the
|
||||
// run, so a second module asking for a different set of counters would be measured against a
|
||||
// snapshot that never collected them: its difference comes out as the process lifetime
|
||||
// average rather than a rate, and the `newItem->... < oldItem->...` checks in
|
||||
// `ffDetectTopProcesses` cannot tell, because the missing counters read as 0 there. This is
|
||||
// a configuration fastfetch cannot serve, so it is refused rather than printed wrong.
|
||||
if (showTypes != preparedTypes) {
|
||||
fputs("Error: `top` modules with different `showTypes` cannot share a run\n", stderr);
|
||||
fputs(" The baseline snapshot is collected once and reused, so it only holds the counters the first module asked for.\n", stderr);
|
||||
fputs(" Give the modules the same `showTypes`, or run them in separate invocations.\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// `showTypes` cannot change between this call and `ffDetectTopProcesses`: `ffPrepareCommandOption`
|
||||
// and `parseStructureCommand` both build the options through `initStructureModuleOptions`, which
|
||||
// merges the module object from the JSON config. So the baseline always matches what the second
|
||||
// snapshot collects and needs no re-validation.
|
||||
// Within one module `showTypes` cannot change between this call and `ffDetectTopProcesses`:
|
||||
// `ffPrepareCommandOption` and `parseStructureCommand` both build the options through
|
||||
// `initStructureModuleOptions`, which merges the module object from the JSON config, so the
|
||||
// baseline always matches what the second snapshot collects.
|
||||
preparedTypes = showTypes;
|
||||
ffListInit(&first);
|
||||
startTick = ffTimeGetTick();
|
||||
ffTopGetProcessSnapshot(&first, showTypes);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "wallpaper.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/apple/osascript.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
@@ -8,7 +7,9 @@
|
||||
const char* detectFromPlist(FFstrbuf* result) {
|
||||
// For Sonoma and later (macOS 14.0+)
|
||||
// https://github.com/JohnCoates/Aerial/issues/1332
|
||||
NSError* error;
|
||||
// Initialised because the check below reads it: the method is documented to assign it, but an
|
||||
// uninitialised read decides between two sources of the wallpaper, so it is not worth relying on.
|
||||
NSError* error = nil;
|
||||
NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Application Support/com.apple.wallpaper/Store/Index.plist", instance.state.platform.homeDir.chars];
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName]
|
||||
error:&error];
|
||||
|
||||
@@ -894,8 +894,21 @@ static const char* callIntMethod(FFBinder* binder, uint32_t handle, const char*
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// `getOwnPackage` writes at most this many bytes, terminator included.
|
||||
#define FF_WIFI_ANDROID_PACKAGE_SIZE 128
|
||||
|
||||
// The parcel for `getConnectionInfo` is the interface token, the package name and the -1 that
|
||||
// stands for a null `callingFeatureId`. A string16 costs 4 + 2 * (length + 1) bytes padded to four,
|
||||
// and the token puts three int32 in front of the descriptor. Deriving the size from the two names
|
||||
// rather than rounding it up is what keeps the longest package name `getOwnPackage` can produce
|
||||
// from marking the parcel truncated -- that fails the whole detection, rather than degrading to a
|
||||
// call without a name.
|
||||
#define FF_WIFI_ANDROID_PARCEL_SIZE \
|
||||
(12 + ((4 + 2 * sizeof(FF_WIFI_ANDROID_DESCRIPTOR) + 3) & ~3u) \
|
||||
+ ((4 + 2 * FF_WIFI_ANDROID_PACKAGE_SIZE + 3) & ~3u) + 4)
|
||||
|
||||
static const char* detectWithBinder(FFlist* result) {
|
||||
char package[128];
|
||||
char package[FF_WIFI_ANDROID_PACKAGE_SIZE];
|
||||
if (!getOwnPackage(package, sizeof(package))) {
|
||||
return "Cannot determine the package name of this process";
|
||||
}
|
||||
@@ -913,15 +926,17 @@ static const char* detectWithBinder(FFlist* result) {
|
||||
return error;
|
||||
}
|
||||
|
||||
uint32_t handle = 0;
|
||||
error = ffBinderLookupService(&binder, FF_WIFI_ANDROID_SERVICE, FF_BINDER_SM_GET_SERVICE, &handle);
|
||||
// Released on every path out of this function, including the ones below that return early: the
|
||||
// references the lookup takes are the process's only claim on the service node.
|
||||
[[gnu::cleanup(ffBinderServiceHandleRelease)]] FFBinderServiceHandle service = { .binder = &binder };
|
||||
error = ffBinderLookupService(&binder, FF_WIFI_ANDROID_SERVICE, FF_BINDER_SM_GET_SERVICE, &service.handle);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
FF_DEBUG("The \"%s\" service is handle %u, %s is transaction %d",
|
||||
FF_WIFI_ANDROID_SERVICE, handle, FF_WIFI_ANDROID_GET_CONNECTION_INFO, transaction);
|
||||
FF_WIFI_ANDROID_SERVICE, service.handle, FF_WIFI_ANDROID_GET_CONNECTION_INFO, transaction);
|
||||
|
||||
uint8_t parcelBuffer[256];
|
||||
uint8_t parcelBuffer[FF_WIFI_ANDROID_PARCEL_SIZE];
|
||||
FFBinderParcel parcel = ffBinderParcelCreate(parcelBuffer, sizeof(parcelBuffer));
|
||||
ffBinderParcelPutInterfaceToken(&parcel, FF_WIFI_ANDROID_DESCRIPTOR);
|
||||
ffBinderParcelPutString16(&parcel, package);
|
||||
@@ -930,7 +945,7 @@ static const char* detectWithBinder(FFlist* result) {
|
||||
|
||||
uint8_t replyBuffer[FF_WIFI_ANDROID_REPLY_SIZE];
|
||||
FFBinderReply reply = ffBinderReplyCreate(replyBuffer, sizeof(replyBuffer));
|
||||
error = ffBinderTransact(&binder, handle, (uint32_t) transaction, &parcel, &reply);
|
||||
error = ffBinderTransact(&binder, service.handle, (uint32_t) transaction, &parcel, &reply);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
@@ -957,7 +972,7 @@ static const char* detectWithBinder(FFlist* result) {
|
||||
// that is up. WIFI_STATE_DISABLING is a radio that is still up, so only the one value means
|
||||
// down, and WIFI_STATE_UNKNOWN is the service declining to say.
|
||||
int32_t state = 0;
|
||||
const char* stateError = callIntMethod(&binder, handle, FF_WIFI_ANDROID_GET_WIFI_ENABLED_STATE, &state);
|
||||
const char* stateError = callIntMethod(&binder, service.handle, FF_WIFI_ANDROID_GET_WIFI_ENABLED_STATE, &state);
|
||||
if (stateError == nullptr && state != FF_WIFI_ANDROID_WIFI_STATE_UNKNOWN) {
|
||||
connection.up = state != FF_WIFI_ANDROID_WIFI_STATE_DISABLED;
|
||||
connection.upKnown = true;
|
||||
|
||||
Reference in New Issue
Block a user