- A system information fetch tool (or neofetch like program), which its focus point is the perfomance and customizability
+ A system information fetch tool (or neofetch like program), which its focus point is the performance and customizability
@@ -30,12 +30,20 @@
+
+ It's even an android widget and GTK3 app
+
+
+
+
+
+
+
## Key Features
-
-* **GUI mode (GTK3)**: Run customfetch even as a GUI application
+* Run customfetch as a **terminal** or **GTK3 application** or even as an **android widget**
* Really easy customizable and fast, check [Config (with explanation)](#config-with-explanation) section
-* Super lightweight, 3.1MB max
+* Super lightweight, 3.1MB max (compiled as terminal and GTK3 app for desktop)
## Depends
currently requires **C++20**, but it's possible to compile with C++17 too (not officially supported)
@@ -94,6 +102,14 @@ yay -S customfetch-git
yay -S customfetch-gui-git
```
+## Android app (source)
+```bash
+# need java 17 + gradle 8.9 installed.
+# It's suggested to build from android studio,
+# so you that you can install the NDK library
+./android/gradlew assembleDebug --project-dir=./android
+```
+
### Compile from (source) (unstable)
```bash
# clone the git dir
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 00000000..aa724b77
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/android/.idea/.gitignore b/android/.idea/.gitignore
new file mode 100644
index 00000000..26d33521
--- /dev/null
+++ b/android/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/android/.idea/.name b/android/.idea/.name
new file mode 100644
index 00000000..f42fe1a8
--- /dev/null
+++ b/android/.idea/.name
@@ -0,0 +1 @@
+customfetch_android
\ No newline at end of file
diff --git a/android/.idea/codeStyles/Project.xml b/android/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..a27d1e9c
--- /dev/null
+++ b/android/.idea/codeStyles/Project.xml
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/codeStyles/codeStyleConfig.xml b/android/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..79ee123c
--- /dev/null
+++ b/android/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/compiler.xml b/android/.idea/compiler.xml
new file mode 100644
index 00000000..b86273d9
--- /dev/null
+++ b/android/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/deploymentTargetSelector.xml b/android/.idea/deploymentTargetSelector.xml
new file mode 100644
index 00000000..b268ef36
--- /dev/null
+++ b/android/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/gradle.xml b/android/.idea/gradle.xml
new file mode 100644
index 00000000..7b3006b6
--- /dev/null
+++ b/android/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/kotlinc.xml b/android/.idea/kotlinc.xml
new file mode 100644
index 00000000..148fdd24
--- /dev/null
+++ b/android/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/migrations.xml b/android/.idea/migrations.xml
new file mode 100644
index 00000000..f8051a6f
--- /dev/null
+++ b/android/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/misc.xml b/android/.idea/misc.xml
new file mode 100644
index 00000000..b2c751a3
--- /dev/null
+++ b/android/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/runConfigurations.xml b/android/.idea/runConfigurations.xml
new file mode 100644
index 00000000..16660f1d
--- /dev/null
+++ b/android/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/.idea/vcs.xml b/android/.idea/vcs.xml
new file mode 100644
index 00000000..6c0b8635
--- /dev/null
+++ b/android/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt
new file mode 100644
index 00000000..2d98a764
--- /dev/null
+++ b/android/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Same file as the one before, just used to be compiled for android
+
+cmake_minimum_required(VERSION 3.10)
+
+project(customfetch)
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb3 -O0 -DDEBUG=1 -Wall -Wextra -Wpedantic -Wno-unused-parameter")
+
+include_directories(${CMAKE_SOURCE_DIR}/../include)
+
+file(GLOB
+ SRC
+ "${CMAKE_SOURCE_DIR}/../src/*.cpp"
+ "${CMAKE_SOURCE_DIR}/../src/query/unix/*.cpp"
+ "${CMAKE_SOURCE_DIR}/../src/query/android/*.cpp"
+ "${CMAKE_SOURCE_DIR}/../src/query/unix/utils/*.cpp"
+ "${CMAKE_SOURCE_DIR}/../src/toml++/toml.cpp"
+ "app/src/main/cpp/customfetch_android.cpp"
+)
+
+add_library(customfetch SHARED ${SRC})
+
+# get git branch
+execute_process(
+ COMMAND git rev-parse --abbrev-ref HEAD
+ OUTPUT_VARIABLE GIT_BRANCH
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+set(VERSION "0.10.2")
+target_compile_definitions(customfetch PRIVATE
+ VERSION="${VERSION}"
+ BRANCH="${GIT_BRANCH}"
+ DANDROID_APP=1
+)
+
+# fmt
+add_library(fmt STATIC
+ "${CMAKE_SOURCE_DIR}/../src/fmt/os.cc"
+ "${CMAKE_SOURCE_DIR}/../src/fmt/format.cc")
+
+# add library
+target_link_libraries(customfetch fmt log android)
diff --git a/android/app/.gitignore b/android/app/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/android/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
new file mode 100644
index 00000000..c4abc0b5
--- /dev/null
+++ b/android/app/build.gradle.kts
@@ -0,0 +1,87 @@
+import java.util.Properties
+
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+}
+
+android {
+ namespace = "org.toni.customfetch_android"
+ compileSdk = 35
+
+ defaultConfig {
+ applicationId = "org.toni.customfetch_android"
+ minSdk = 26
+ targetSdk = 35
+ versionCode = 1
+ versionName = "0.10.2"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags += "-I${rootDir}/../include -DANDROID_APP=1"
+ targets("customfetch")
+ }
+ }
+ }
+
+ signingConfigs {
+ create("release") {
+ val propertiesFile = rootProject.file("signing.properties")
+ if (propertiesFile.exists()) {
+ val properties = Properties().apply {
+ load(propertiesFile.reader())
+ }
+ storeFile = File(properties.getProperty("storeFilePath"))
+ storePassword = properties.getProperty("storePassword")
+ keyPassword = properties.getProperty("keyPassword")
+ keyAlias = properties.getProperty("keyAlias")
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ getByName("release") {
+ signingConfig = signingConfigs.getByName("release")
+ }
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ viewBinding = true
+ }
+ externalNativeBuild {
+ cmake {
+ path = file("../CMakeLists.txt")
+ version = "3.22.1"
+ }
+ }
+}
+
+dependencies {
+ implementation(libs.colorpickerview)
+
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.material)
+ implementation(libs.androidx.constraintlayout)
+ implementation(libs.androidx.navigation.fragment.ktx)
+ implementation(libs.androidx.navigation.ui.ktx)
+ implementation(libs.androidx.preference.ktx)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+}
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/android/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..16ea7d90
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/assets b/android/app/src/main/assets
new file mode 120000
index 00000000..2b56518e
--- /dev/null
+++ b/android/app/src/main/assets
@@ -0,0 +1 @@
+../../../../assets
\ No newline at end of file
diff --git a/android/app/src/main/cpp/customfetch_android.cpp b/android/app/src/main/cpp/customfetch_android.cpp
new file mode 100644
index 00000000..6bc4ad4d
--- /dev/null
+++ b/android/app/src/main/cpp/customfetch_android.cpp
@@ -0,0 +1,19 @@
+#include
+#include
+#include
+#include "util.hpp"
+
+#define ARRAY_SIZE(x) sizeof(x) / sizeof(x[0])
+
+extern std::string mainAndroid_and_render(int argc, char *argv[], JNIEnv *env, jobject obj, bool do_not_load_config);
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_org_toni_customfetch_1android_widget_CustomfetchMainRender_mainAndroid(JNIEnv *env, jobject obj, jstring args, jboolean do_not_load_config) {
+ const std::string& str_args = env->GetStringUTFChars(args, nullptr);
+ const std::vector& tokens = split(str_args, ' ');
+ char *argv[tokens.size()];
+ for (size_t i = 0; i < tokens.size(); ++i)
+ argv[i] = strdup(tokens[i].c_str());
+
+ return env->NewStringUTF(mainAndroid_and_render(ARRAY_SIZE(argv), argv, env, obj, do_not_load_config).c_str());
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/toni/customfetch_android/AboutMeFragment.kt b/android/app/src/main/java/org/toni/customfetch_android/AboutMeFragment.kt
new file mode 100644
index 00000000..e0b78116
--- /dev/null
+++ b/android/app/src/main/java/org/toni/customfetch_android/AboutMeFragment.kt
@@ -0,0 +1,44 @@
+package org.toni.customfetch_android
+
+import android.os.Bundle
+import androidx.core.text.HtmlCompat
+import android.text.method.LinkMovementMethod
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import org.toni.customfetch_android.databinding.AboutMeFragmentBinding
+
+class AboutMeFragment : Fragment() {
+
+ private var _binding: AboutMeFragmentBinding? = null
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = AboutMeFragmentBinding.inflate(inflater, container, false)
+ binding.toolbar.apply {
+ setNavigationIcon(R.drawable.arrow_back)
+ setNavigationOnClickListener { _ ->
+ requireActivity().supportFragmentManager.popBackStack()
+ }
+ }
+
+ binding.burntGithubLink.movementMethod = LinkMovementMethod.getInstance()
+ binding.toniGithubLink.movementMethod = LinkMovementMethod.getInstance()
+ binding.bcppDiscordLink.movementMethod = LinkMovementMethod.getInstance()
+ binding.fmtlibGithubLink.movementMethod = LinkMovementMethod.getInstance()
+ binding.tomlLibGithubLink.movementMethod = LinkMovementMethod.getInstance()
+
+ return binding.root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/toni/customfetch_android/MainActivity.kt b/android/app/src/main/java/org/toni/customfetch_android/MainActivity.kt
new file mode 100644
index 00000000..59c8587d
--- /dev/null
+++ b/android/app/src/main/java/org/toni/customfetch_android/MainActivity.kt
@@ -0,0 +1,151 @@
+package org.toni.customfetch_android
+
+import android.content.Intent
+import android.content.res.AssetManager
+import android.os.Build
+import android.os.Bundle
+import android.os.Environment
+import android.provider.Settings
+import android.text.method.LinkMovementMethod
+import android.util.Log
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.FragmentTransaction
+import org.toni.customfetch_android.databinding.ActivityMainBinding
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.nio.file.Files
+import kotlin.io.path.Path
+
+// kinda magic numbers
+const val TEST_CONFIG_FILE_RC = 2
+
+class MainActivity : AppCompatActivity() {
+ private lateinit var binding: ActivityMainBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (!Environment.isExternalStorageManager()) {
+ val alert = AlertDialog.Builder(this)
+ .setTitle("Grant external storage management permission")
+ .setMessage("Customfetch needs permissions to manage external storage to able to access config files.\n"+
+ "By default we going to read/write the following directories:\n"+
+ "/storage/emulated/0/.config/\n"+
+ "/storage/emulated/0/.config/customfetch/")
+ .setPositiveButton("Grant permission"
+ ) { _, _ ->
+ val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
+ startActivity(intent)
+ }
+ .setIcon(R.drawable.icon_alert_yellow)
+
+ val view: View = layoutInflater.inflate(R.layout.grant_perm, null, false)
+ alert.setView(view)
+ alert.show()
+ }
+ }
+
+ if (!Files.exists(Path(filesDir.absolutePath + "/ascii")))
+ copyToAssetFolder(assets, filesDir.absolutePath, "ascii")
+
+ binding.discordLink.movementMethod = LinkMovementMethod.getInstance()
+ binding.redditLink.movementMethod = LinkMovementMethod.getInstance()
+
+ binding.testConfigFile.setOnClickListener { _ ->
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "*/*"
+ }
+ startActivityForResult(intent, TEST_CONFIG_FILE_RC)
+ }
+
+ binding.aboutMe.setOnClickListener { _ ->
+ val fragment = AboutMeFragment()
+ val transaction: FragmentTransaction =
+ supportFragmentManager.beginTransaction()
+ transaction.replace(android.R.id.content, fragment)
+ transaction.addToBackStack(null).commit()
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (resultCode == RESULT_OK && data != null && data.data != null) {
+ data.data?.let { uri ->
+ when(requestCode) {
+ TEST_CONFIG_FILE_RC -> {
+ val fragment = TestConfigFragment().apply {
+ configFile = PathUtil.getPath(this@MainActivity, uri)
+ }
+ val transaction: FragmentTransaction =
+ supportFragmentManager.beginTransaction()
+ transaction.replace(android.R.id.content, fragment)
+ transaction.addToBackStack(null).commit()
+ }
+ else -> {}
+ }
+ }
+ }
+ }
+}
+
+const val TAG: String = "AssetCopy"
+
+internal fun copyToAssetFolder(assets: AssetManager, absolutePath: String, assetSubFolder: String) {
+ try {
+ copyDirectory(assets, assetSubFolder, absolutePath)
+ Log.d(TAG, "All files copied to: $absolutePath")
+ } catch (e: IOException) {
+ Log.e(TAG, "Failed to copy asset folder: $assetSubFolder", e)
+ }
+}
+
+@Throws(IOException::class)
+private fun copyDirectory(
+ assetManager: AssetManager,
+ sourceDir: String,
+ destinationDir: String
+) {
+ val files = assetManager.list(sourceDir)
+ if (files.isNullOrEmpty())
+ return
+
+ val destDir = File(destinationDir, sourceDir)
+ if (!destDir.exists() && !destDir.mkdirs())
+ throw IOException("Failed to create directory: ${destDir.absolutePath}")
+
+ for (fileName in files) {
+ val assetPath = "$sourceDir/$fileName"
+ val destPath = destDir.path + "/" + fileName
+ if (isDirectory(assetManager, assetPath))
+ copyDirectory(assetManager, assetPath, destinationDir)
+ else
+ copyFile(assetManager, assetPath, destPath)
+ }
+}
+
+@Throws(IOException::class)
+private fun isDirectory(assetManager: AssetManager, path: String): Boolean {
+ val files = assetManager.list(path)
+ return !files.isNullOrEmpty()
+}
+
+@Throws(IOException::class)
+private fun copyFile(assetManager: AssetManager, assetPath: String, destPath: String) {
+ assetManager.open(assetPath).use { `in` ->
+ FileOutputStream(destPath).use { out ->
+ val buffer = ByteArray(8192)
+ var bytesRead: Int
+ while ((`in`.read(buffer).also { bytesRead = it }) != -1)
+ out.write(buffer, 0, bytesRead)
+
+ Log.d(TAG, "File copied: $destPath")
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/toni/customfetch_android/PathUtil.java b/android/app/src/main/java/org/toni/customfetch_android/PathUtil.java
new file mode 100644
index 00000000..84337abb
--- /dev/null
+++ b/android/app/src/main/java/org/toni/customfetch_android/PathUtil.java
@@ -0,0 +1,98 @@
+package org.toni.customfetch_android;
+
+import android.annotation.SuppressLint;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+import java.net.URISyntaxException;
+
+/**
+ * Created by Aki on 1/7/2017.
+ * original
+ */
+
+public class PathUtil {
+ /*
+ * Gets the file path of the given Uri.
+ */
+ @SuppressLint("NewApi")
+ public static String getPath(Context context, Uri uri) throws URISyntaxException {
+ final boolean needToCheckUri = Build.VERSION.SDK_INT >= 19;
+ String selection = null;
+ String[] selectionArgs = null;
+ // Uri is different in versions after KITKAT (Android 4.4), we need to
+ // deal with different Uris.
+ if (needToCheckUri && DocumentsContract.isDocumentUri(context.getApplicationContext(), uri)) {
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ } else if (isDownloadsDocument(uri)) {
+ final String id = DocumentsContract.getDocumentId(uri);
+ uri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+ } else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+ if ("image".equals(type)) {
+ uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+ selection = "_id=?";
+ selectionArgs = new String[]{ split[1] };
+ }
+ }
+ if ("content".equalsIgnoreCase(uri.getScheme())) {
+ String[] projection = { MediaStore.Images.Media.DATA };
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+ assert cursor != null;
+ int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ if (cursor.moveToFirst()) {
+ return cursor.getString(column_index);
+ }
+ cursor.close();
+ } catch (Exception e) {
+ }
+ } else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+ return null;
+ }
+
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/toni/customfetch_android/TestConfigFragment.kt b/android/app/src/main/java/org/toni/customfetch_android/TestConfigFragment.kt
new file mode 100644
index 00000000..951d9d5a
--- /dev/null
+++ b/android/app/src/main/java/org/toni/customfetch_android/TestConfigFragment.kt
@@ -0,0 +1,66 @@
+package org.toni.customfetch_android
+
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.os.Bundle
+import android.text.SpannableString
+import android.text.TextPaint
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.text.HtmlCompat
+import androidx.fragment.app.Fragment
+import org.toni.customfetch_android.databinding.TestConfigFragmentBinding
+import org.toni.customfetch_android.widget.customfetchRender
+
+
+class TestConfigFragment : Fragment() {
+
+ private var _binding: TestConfigFragmentBinding? = null
+ // This property is only valid between onCreateView and
+ // onDestroyView.
+ private val binding get() = _binding!!
+
+ var configFile = ""
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = TestConfigFragmentBinding.inflate(inflater, container, false)
+ binding.toolbar.apply {
+ setNavigationIcon(R.drawable.arrow_back)
+ setNavigationOnClickListener { _ ->
+ requireActivity().supportFragmentManager.popBackStack()
+ }
+ }
+
+ val result = customfetchRender.getParsedContent(
+ AppCompatActivity(),
+ 0,
+ 0f,
+ false,
+ TextPaint(),
+ "-C $configFile -Nnm \$", // this is the important thing
+ false
+ )
+
+ if (result.contentEquals("android")) {
+ binding.titleResult.setTextColor(Color.GREEN)
+ binding.titleResult.text = "SUCCESS"
+ binding.testConfigResult.text = "config file '$configFile' works!!"
+ } else {
+ binding.titleResult.setTextColor(Color.RED)
+ binding.titleResult.text = "FAILURE"
+ binding.testConfigResult.text = result
+ }
+
+ return binding.root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/org/toni/customfetch_android/widget/customfetch.kt b/android/app/src/main/java/org/toni/customfetch_android/widget/customfetch.kt
new file mode 100644
index 00000000..11638e11
--- /dev/null
+++ b/android/app/src/main/java/org/toni/customfetch_android/widget/customfetch.kt
@@ -0,0 +1,133 @@
+package org.toni.customfetch_android.widget
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.content.Context
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.os.Bundle
+import android.text.TextPaint
+import android.util.Log
+import android.widget.RemoteViews
+import org.toni.customfetch_android.R
+
+/**
+ * Implementation of App Widget functionality.
+ * App Widget Configuration implemented in [customfetchConfigureActivity]
+ */
+class customfetch : AppWidgetProvider() {
+ var firstFun = true
+ override fun onUpdate(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray
+ ) {
+ // There may be multiple widgets active, so update all of them
+ if (!firstFun)
+ for (appWidgetId in appWidgetIds)
+ updateAppWidget(context, appWidgetManager, appWidgetId)
+ firstFun = false
+ }
+
+ override fun onDeleted(context: Context, appWidgetIds: IntArray) {
+ // When the user deletes the widget, delete the preference associated with it.
+ for (appWidgetId in appWidgetIds) {
+ deleteConfigPrefs(context, appWidgetId)
+ }
+ }
+
+ override fun onAppWidgetOptionsChanged(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ newOptions: Bundle
+ ) {
+ updateAppWidget(context, appWidgetManager, appWidgetId)
+ }
+
+ override fun onEnabled(context: Context) {
+ // Enter relevant functionality for when the first widget is created
+
+ }
+
+ override fun onDisabled(context: Context) {
+ // Enter relevant functionality for when the last widget is disabled
+ }
+}
+
+// https://stackoverflow.com/a/58501760
+class WidgetSizeProvider(
+ private val context: Context // Do not pass Application context
+) {
+
+ private val appWidgetManager = AppWidgetManager.getInstance(context)
+
+ fun getWidgetsSize(widgetId: Int): Pair {
+ val isPortrait = (context.resources.configuration.orientation == ORIENTATION_PORTRAIT)
+ val width = getWidgetWidth(isPortrait, widgetId)
+ val height = getWidgetHeight(isPortrait, widgetId)
+ val widthInPx = context.dip(width)
+ val heightInPx = context.dip(height)
+ return widthInPx to heightInPx
+ }
+
+ private fun getWidgetWidth(isPortrait: Boolean, widgetId: Int): Int =
+ if (isPortrait) {
+ getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
+ } else {
+ getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
+ }
+
+ private fun getWidgetHeight(isPortrait: Boolean, widgetId: Int): Int =
+ if (isPortrait) {
+ getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
+ } else {
+ getWidgetSizeInDp(widgetId, AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
+ }
+
+ private fun getWidgetSizeInDp(widgetId: Int, key: String): Int =
+ appWidgetManager.getAppWidgetOptions(widgetId).getInt(key, 0)
+
+ private fun Context.dip(value: Int): Int = (value * resources.displayMetrics.density).toInt()
+
+}
+
+internal fun updateAppWidget(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int
+
+) {
+ val disableLineWrap = getDisableLineWrap(context, appWidgetId)
+ val bgColor = getBgColor(context, appWidgetId)
+
+ // create a TextPaint to be used to measure text size
+ val textPaint = TextPaint()
+ val textSizeSp = 8f
+ val textSizePx = textSizeSp * context.resources.displayMetrics.scaledDensity
+ textPaint.textSize = textSizePx
+
+ val additionalTruncateWidth = getTruncateWidthPref(context, appWidgetId).toFloat()
+ var width = WidgetSizeProvider(context).getWidgetsSize(appWidgetId).first.toFloat()
+ if (additionalTruncateWidth > 0.20)
+ width *= additionalTruncateWidth
+
+ Log.d("widthTesting", "textSizePx = $textSizePx")
+ Log.d("widthTesting", "width = $width")
+
+ val parsedContent =
+ customfetchRender.getParsedContent(
+ context,
+ appWidgetId,
+ width,
+ disableLineWrap,
+ textPaint
+ )
+
+ // Construct the RemoteViews object
+ val views = RemoteViews(context.packageName, R.layout.customfetch)
+ views.setTextViewText(R.id.customfetch_text, parsedContent)
+ views.setInt(R.id.widget_root, "setBackgroundColor", bgColor);
+
+ // Instruct the widget manager to update the widget
+ appWidgetManager.updateAppWidget(appWidgetId, views)
+}
diff --git a/android/app/src/main/java/org/toni/customfetch_android/widget/customfetchConfigureActivity.kt b/android/app/src/main/java/org/toni/customfetch_android/widget/customfetchConfigureActivity.kt
new file mode 100644
index 00000000..4e36c626
--- /dev/null
+++ b/android/app/src/main/java/org/toni/customfetch_android/widget/customfetchConfigureActivity.kt
@@ -0,0 +1,288 @@
+package org.toni.customfetch_android.widget
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.appwidget.AppWidgetManager
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.text.Editable
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import android.text.TextPaint
+import android.text.TextUtils.TruncateAt
+import android.text.TextUtils.ellipsize
+import android.text.TextWatcher
+import android.util.TypedValue
+import android.view.View
+import android.widget.CheckBox
+import android.widget.EditText
+import android.widget.LinearLayout
+import android.widget.RadioGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.graphics.toColorInt
+import androidx.core.text.HtmlCompat
+import com.skydoves.colorpickerview.ColorPickerView
+import com.skydoves.colorpickerview.listeners.ColorEnvelopeListener
+import com.skydoves.colorpickerview.sliders.AlphaSlideBar
+import com.skydoves.colorpickerview.sliders.BrightnessSlideBar
+import org.toni.customfetch_android.R
+import org.toni.customfetch_android.databinding.CustomfetchConfigureBinding
+import java.io.File
+import java.nio.file.Files
+import kotlin.io.path.Path
+
+
+/**
+ * The configuration screen for the [customfetch] AppWidget.
+ */
+class customfetchConfigureActivity : Activity() {
+ private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
+ private lateinit var binding: CustomfetchConfigureBinding
+ private var onAddWidget = View.OnClickListener {
+ val context = this@customfetchConfigureActivity
+
+ // When the button is clicked, store the string locally
+ saveConfigPrefs(
+ context,
+ appWidgetId,
+ binding.argumentsConfigure.text.toString(),
+ binding.additionalTruncateWidth.text.toString(),
+ disableLineWrap,
+ bgColor)
+
+ // It is the responsibility of the configuration activity to update the app widget
+ val appWidgetManager = AppWidgetManager.getInstance(context)
+ updateAppWidget(context, appWidgetManager, appWidgetId)
+
+ // Make sure we pass back the original appWidgetId
+ val resultValue = Intent()
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
+ setResult(RESULT_OK, resultValue)
+ finish()
+ }
+
+ // truncate text
+ private var disableLineWrap = false
+ private var bgColor = 0
+
+ @SuppressLint("SetTextI18n", "ClickableViewAccessibility")
+ public override fun onCreate(icicle: Bundle?) {
+ super.onCreate(icicle)
+
+ // Set the result to CANCELED. This will cause the widget host to cancel
+ // out of the widget placement if the user presses the back button.
+ setResult(RESULT_CANCELED)
+
+ binding = CustomfetchConfigureBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ binding.addButton.setOnClickListener(onAddWidget)
+
+ // Find the widget id from the intent.
+ val extras = intent.extras
+ if (extras != null) {
+ appWidgetId = extras.getInt(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID
+ )
+ }
+
+ // If this activity was started with an intent without an app widget ID, finish with an error.
+ if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish()
+ return
+ }
+
+ binding.argumentsConfigure.setText(getArgsPref(this@customfetchConfigureActivity, appWidgetId))
+ binding.additionalTruncateWidth.setText("0.6")
+ binding.argsHelp.text = customfetchRender.mainAndroid("customfetch --help", true)
+
+ binding.showModulesList.setOnCheckedChangeListener { _, isChecked ->
+ binding.argsHelp.text = customfetchRender.mainAndroid("customfetch ${if (isChecked) "-l" else "-h"}", true)
+ }
+
+ binding.disableWrapLinesCheck.setOnCheckedChangeListener { _, isChecked ->
+ disableLineWrap = isChecked
+ }
+
+ binding.selectBgColor.setOnCheckedChangeListener { _, checkedId ->
+ when (checkedId) {
+ R.id.radio_system_bg_color -> {
+ binding.customColorSelect.visibility = View.GONE
+ val typedValue = TypedValue()
+ this.theme.resolveAttribute(android.R.attr.colorBackground, typedValue, true)
+ bgColor = typedValue.data
+ }
+
+ R.id.radio_transparent_bg -> {
+ binding.customColorSelect.visibility = View.GONE
+ bgColor = 0x00FFFFFF
+ }
+
+ R.id.radio_custom_colors -> {
+ binding.customColorSelect.visibility = View.VISIBLE
+ // disable scroll when interacting with the color picker
+ binding.colorPickerView.setOnTouchListener { view, _ ->
+ view.parent.requestDisallowInterceptTouchEvent(true)
+ false // allow colorPickerView to handle the touch event
+ }
+
+ // if modified edittext and it's valid, apply to the preview
+ // else if modified in the color picker, apply to the edittext
+ var hexColor = ""
+ binding.colorPickerHex.addTextChangedListener (object : TextWatcher {
+ override fun afterTextChanged(s: Editable) {
+ val col = s.toString()
+ if (isValidHex(col)) {
+ binding.colorPreview.setBackgroundColor(Color.parseColor(col))
+ binding.colorPickerView.setInitialColor(col.toColorInt())
+ hexColor = col
+ bgColor = col.toColorInt()
+ }
+ }
+ override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
+ override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
+ })
+ var firstRun = true
+ binding.colorPickerView.setColorListener(ColorEnvelopeListener { envelope, _ ->
+ if (firstRun)
+ hexColor = "#${envelope.hexCode}"
+
+ if (hexColor != "#${envelope.hexCode}") {
+ binding.colorPickerHex.setText("#${envelope.hexCode}")
+ hexColor = "#${envelope.hexCode}"
+ }
+ binding.colorPreview.setBackgroundColor(envelope.color)
+ bgColor = envelope.color
+ firstRun = false
+ })
+ binding.colorPickerView.attachAlphaSlider(binding.alphaSlideBar)
+ binding.colorPickerView.attachBrightnessSlider(binding.brightnessSlideBar)
+ }
+ }
+ }
+ }
+
+ private fun isValidHex(color: String): Boolean =
+ color.matches("^#[0-9A-Fa-f]{8}$".toRegex())
+}
+
+class CustomfetchMainRender {
+ fun getParsedContent(
+ context: Context,
+ appWidgetId: Int,
+ width: Float,
+ disableLineWrap: Boolean,
+ paint: TextPaint,
+ otherArguments: String = "",
+ postToast: Boolean = true,
+ doNotLoadConfig: Boolean = false
+ ): SpannableStringBuilder {
+ val parsedContent = SpannableStringBuilder()
+ val arguments = otherArguments.ifEmpty {
+ getArgsPref(context, appWidgetId)
+ }
+ val htmlContent = mainAndroid("customfetch $arguments", doNotLoadConfig)
+
+ val errorFile = "/storage/emulated/0/.config/customfetch/error_log.txt"
+ val errorLock = "/storage/emulated/0/.config/customfetch/error.lock"
+ if (Files.exists(Path(errorLock))) {
+ val file = File(errorLock)
+ val error = file.bufferedReader().use { it.readText() }
+ if (postToast) {
+ val handler = Handler(Looper.getMainLooper())
+ handler.post {
+ Toast.makeText(context, error, Toast.LENGTH_LONG).show()
+ }
+ handler.post {
+ Toast.makeText(context, "read error logs at $errorFile", Toast.LENGTH_LONG)
+ .show()
+ }
+ }
+ file.delete()
+ parsedContent.append("read error logs at $errorFile\n\n$error")
+ return parsedContent
+ }
+
+ if (disableLineWrap) {
+ val eachLine = htmlContent!!.split(" ").map { it.trim() }
+ for (line in eachLine) {
+ var parsedLine = HtmlCompat.fromHtml(line, HtmlCompat.FROM_HTML_MODE_COMPACT)
+ parsedLine =
+ ellipsize(parsedLine, paint, width, TruncateAt.END) as Spanned
+ parsedContent.appendLine(parsedLine)
+ }
+ } else {
+ parsedContent.append(htmlContent?.let {
+ HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_COMPACT)
+ })
+ }
+
+ return parsedContent
+ }
+
+ external fun mainAndroid(argv: String, doNotLoadConfig: Boolean): String?
+ companion object {
+ init {
+ System.loadLibrary("customfetch")
+ }
+ }
+}
+val customfetchRender = CustomfetchMainRender()
+
+private const val PREFS_NAME = "org.toni.customfetch_android.customfetch"
+private const val PREF_PREFIX_KEY = "appwidget_"
+
+// Save the preferences to the SharedPreferences object for this widget
+internal fun saveConfigPrefs(
+ context: Context,
+ appWidgetId: Int,
+ args: String,
+ truncateWidth: String,
+ disableLineWrap: Boolean,
+ bgColor: Int
+) {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
+ prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_args", args)
+ prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_truncateWidth", truncateWidth)
+ prefs.putBoolean(PREF_PREFIX_KEY + appWidgetId + "_disableLineWrap", disableLineWrap)
+ prefs.putInt(PREF_PREFIX_KEY + appWidgetId + "_bgColor", bgColor)
+ prefs.apply()
+}
+
+internal fun getArgsPref(context: Context, appWidgetId: Int): String {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0)
+ val args = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_args", null)
+ return args ?: "-D ${context.filesDir.absolutePath} -a small"
+}
+
+internal fun getDisableLineWrap(context: Context, appWidgetId: Int): Boolean {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0)
+ val value = prefs.getBoolean(PREF_PREFIX_KEY + appWidgetId + "_disableLineWrap", false)
+ return value
+}
+
+internal fun getBgColor(context: Context, appWidgetId: Int): Int {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0)
+ val value = prefs.getInt(PREF_PREFIX_KEY + appWidgetId + "_bgColor", 0)
+ return value
+}
+
+internal fun getTruncateWidthPref(context: Context, appWidgetId: Int): String {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0)
+ val value = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_truncateWidth", null)
+ return value ?: "0"
+}
+
+internal fun deleteConfigPrefs(context: Context, appWidgetId: Int) {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId + "_args")
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId + "_disableLineWrap")
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId + "_truncateWidth")
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId + "_bgColor")
+ prefs.apply()
+}
diff --git a/android/app/src/main/res/drawable-night/grant_perm.png b/android/app/src/main/res/drawable-night/grant_perm.png
new file mode 100644
index 00000000..a8740eab
Binary files /dev/null and b/android/app/src/main/res/drawable-night/grant_perm.png differ
diff --git a/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png b/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png
new file mode 100644
index 00000000..429c58b2
Binary files /dev/null and b/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png differ
diff --git a/android/app/src/main/res/drawable-nodpi/grant_perm.png b/android/app/src/main/res/drawable-nodpi/grant_perm.png
new file mode 100644
index 00000000..f6732a37
Binary files /dev/null and b/android/app/src/main/res/drawable-nodpi/grant_perm.png differ
diff --git a/android/app/src/main/res/drawable-v21/app_widget_background.xml b/android/app/src/main/res/drawable-v21/app_widget_background.xml
new file mode 100644
index 00000000..785445c6
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/app_widget_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml b/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml
new file mode 100644
index 00000000..007e2872
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..2b068d11
--- /dev/null
+++ b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/arrow_back.xml b/android/app/src/main/res/drawable/arrow_back.xml
new file mode 100644
index 00000000..af7a5657
--- /dev/null
+++ b/android/app/src/main/res/drawable/arrow_back.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/better_cpp_discord_pfp.png b/android/app/src/main/res/drawable/better_cpp_discord_pfp.png
new file mode 100644
index 00000000..9dcbf538
Binary files /dev/null and b/android/app/src/main/res/drawable/better_cpp_discord_pfp.png differ
diff --git a/android/app/src/main/res/drawable/burnt_github_pfp.png b/android/app/src/main/res/drawable/burnt_github_pfp.png
new file mode 100644
index 00000000..0d5db231
Binary files /dev/null and b/android/app/src/main/res/drawable/burnt_github_pfp.png differ
diff --git a/android/app/src/main/res/drawable/discord_mark_blue.png b/android/app/src/main/res/drawable/discord_mark_blue.png
new file mode 100644
index 00000000..e9dc50d7
Binary files /dev/null and b/android/app/src/main/res/drawable/discord_mark_blue.png differ
diff --git a/android/app/src/main/res/drawable/fmtlib_logo.png b/android/app/src/main/res/drawable/fmtlib_logo.png
new file mode 100644
index 00000000..19c66274
Binary files /dev/null and b/android/app/src/main/res/drawable/fmtlib_logo.png differ
diff --git a/android/app/src/main/res/drawable/github_mark.png b/android/app/src/main/res/drawable/github_mark.png
new file mode 100644
index 00000000..6cb3b705
Binary files /dev/null and b/android/app/src/main/res/drawable/github_mark.png differ
diff --git a/android/app/src/main/res/drawable/github_mark_white.png b/android/app/src/main/res/drawable/github_mark_white.png
new file mode 100644
index 00000000..50b81752
Binary files /dev/null and b/android/app/src/main/res/drawable/github_mark_white.png differ
diff --git a/android/app/src/main/res/drawable/github_pfp.png b/android/app/src/main/res/drawable/github_pfp.png
new file mode 100644
index 00000000..bca86976
Binary files /dev/null and b/android/app/src/main/res/drawable/github_pfp.png differ
diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..07d5da9c
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/icon_alert_yellow.xml b/android/app/src/main/res/drawable/icon_alert_yellow.xml
new file mode 100644
index 00000000..2a895298
--- /dev/null
+++ b/android/app/src/main/res/drawable/icon_alert_yellow.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/reddit_logo_16.png b/android/app/src/main/res/drawable/reddit_logo_16.png
new file mode 100644
index 00000000..ce7f853a
Binary files /dev/null and b/android/app/src/main/res/drawable/reddit_logo_16.png differ
diff --git a/android/app/src/main/res/drawable/round_border.xml b/android/app/src/main/res/drawable/round_border.xml
new file mode 100644
index 00000000..49d670d3
--- /dev/null
+++ b/android/app/src/main/res/drawable/round_border.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/tomlplusplus_banner.png b/android/app/src/main/res/drawable/tomlplusplus_banner.png
new file mode 100644
index 00000000..c9ee9963
Binary files /dev/null and b/android/app/src/main/res/drawable/tomlplusplus_banner.png differ
diff --git a/android/app/src/main/res/layout/about_me_fragment.xml b/android/app/src/main/res/layout/about_me_fragment.xml
new file mode 100644
index 00000000..020c3e3c
--- /dev/null
+++ b/android/app/src/main/res/layout/about_me_fragment.xml
@@ -0,0 +1,259 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..be52e545
--- /dev/null
+++ b/android/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/customfetch.xml b/android/app/src/main/res/layout/customfetch.xml
new file mode 100644
index 00000000..f350c3a0
--- /dev/null
+++ b/android/app/src/main/res/layout/customfetch.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/layout/customfetch_configure.xml b/android/app/src/main/res/layout/customfetch_configure.xml
new file mode 100644
index 00000000..ad5126b9
--- /dev/null
+++ b/android/app/src/main/res/layout/customfetch_configure.xml
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/grant_perm.xml b/android/app/src/main/res/layout/grant_perm.xml
new file mode 100644
index 00000000..57256987
--- /dev/null
+++ b/android/app/src/main/res/layout/grant_perm.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/test_config_fragment.xml b/android/app/src/main/res/layout/test_config_fragment.xml
new file mode 100644
index 00000000..066bcad0
--- /dev/null
+++ b/android/app/src/main/res/layout/test_config_fragment.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 00000000..c209e78e
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..b2dfe3d1
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 00000000..4f0f1d64
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..62b611da
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 00000000..948a3070
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..1b9a6956
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..28d4b77f
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..9287f508
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 00000000..aa7d6427
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 00000000..9126ae37
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/android/app/src/main/res/values-night-v31/themes.xml b/android/app/src/main/res/values-night-v31/themes.xml
new file mode 100644
index 00000000..eebc67ac
--- /dev/null
+++ b/android/app/src/main/res/values-night-v31/themes.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-night/themes.xml b/android/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..e54c2a73
--- /dev/null
+++ b/android/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-v21/styles.xml b/android/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 00000000..13527e2b
--- /dev/null
+++ b/android/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values-v31/colors.xml b/android/app/src/main/res/values-v31/colors.xml
new file mode 100644
index 00000000..eed51668
--- /dev/null
+++ b/android/app/src/main/res/values-v31/colors.xml
@@ -0,0 +1,5 @@
+
+
+ @android:color/system_accent1_500
+ @android:color/system_accent1_800
+
diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml
new file mode 100644
index 00000000..8d4e14be
--- /dev/null
+++ b/android/app/src/main/res/values-v31/styles.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values-v31/themes.xml b/android/app/src/main/res/values-v31/themes.xml
new file mode 100644
index 00000000..8e2b47d2
--- /dev/null
+++ b/android/app/src/main/res/values-v31/themes.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/arrays.xml b/android/app/src/main/res/values/arrays.xml
new file mode 100644
index 00000000..6cf9ed48
--- /dev/null
+++ b/android/app/src/main/res/values/arrays.xml
@@ -0,0 +1,12 @@
+
+
+
+ Reply
+ Reply to all
+
+
+
+ reply
+ reply_all
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/attrs.xml b/android/app/src/main/res/values/attrs.xml
new file mode 100644
index 00000000..7781ac86
--- /dev/null
+++ b/android/app/src/main/res/values/attrs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..03875adb
--- /dev/null
+++ b/android/app/src/main/res/values/colors.xml
@@ -0,0 +1,14 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #FFE1F5FE
+ #FF81D4FA
+ #FF039BE5
+ #FF01579B
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..4db8c590
--- /dev/null
+++ b/android/app/src/main/res/values/dimens.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..82917aef
--- /dev/null
+++ b/android/app/src/main/res/values/strings.xml
@@ -0,0 +1,38 @@
+
+ Customfetch Widget
+
+ SettingsActivity
+
+
+ Messages
+ Sync
+
+
+ Your signature
+ Default reply action
+
+
+ Sync email periodically
+ Download incoming attachments
+ Automatically download attachments for incoming emails
+
+ Only download attachments when manually requested
+ EXAMPLE
+ Configure (insert command line options)
+ Additional truncate text width (insert number bigger than 0.20, or smaller to ignore)
+ Add widget
+ Neofetch like program, which its focus point is the performance and customizability
+ Display modules list
+ Truncate text (may be unstable, use it only on phones)
+ Background color
+ System theme color
+ Transparent background
+ Custom color
+ Join our discord server
+ Better C++ discord server
+ BurntRanch
+ Toni500github
+ fmtlib
+ tomlplusplus
+ Join r/customfetch
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..bc670810
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..0fddf928
--- /dev/null
+++ b/android/app/src/main/res/values/themes.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/xml/backup_rules.xml b/android/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 00000000..fa0f996d
--- /dev/null
+++ b/android/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/xml/customfetch_info.xml b/android/app/src/main/res/xml/customfetch_info.xml
new file mode 100644
index 00000000..52470322
--- /dev/null
+++ b/android/app/src/main/res/xml/customfetch_info.xml
@@ -0,0 +1,14 @@
+
+
diff --git a/android/app/src/main/res/xml/data_extraction_rules.xml b/android/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 00000000..9ee9997b
--- /dev/null
+++ b/android/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/test/java/com/example/settings_testing/ExampleUnitTest.kt b/android/app/src/test/java/com/example/settings_testing/ExampleUnitTest.kt
new file mode 100644
index 00000000..ae0967a3
--- /dev/null
+++ b/android/app/src/test/java/com/example/settings_testing/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.example.settings_testing
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/android/build.gradle.kts b/android/build.gradle.kts
new file mode 100644
index 00000000..922f5511
--- /dev/null
+++ b/android/build.gradle.kts
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+}
\ No newline at end of file
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 00000000..0ef260e9
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,24 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+newArchEnabled=false
diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml
new file mode 100644
index 00000000..e829fe84
--- /dev/null
+++ b/android/gradle/libs.versions.toml
@@ -0,0 +1,34 @@
+[versions]
+agp = "8.7.2"
+colorpickerview = "2.3.0"
+htmlspanner = "0.4"
+kotlin = "1.9.24"
+coreKtx = "1.15.0"
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+appcompat = "1.7.0"
+material = "1.12.0"
+constraintlayout = "2.2.0"
+navigationFragmentKtx = "2.8.3"
+navigationUiKtx = "2.8.3"
+preferenceKtx = "1.2.1"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+colorpickerview = { module = "com.github.skydoves:colorpickerview", version.ref = "colorpickerview" }
+htmlspanner = { module = "com.github.NightWhistler:HtmlSpanner", version.ref = "htmlspanner" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
+androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
+androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preferenceKtx" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..e708b1c0
Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..3a0c85ea
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Nov 15 17:27:43 CET 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/android/gradlew b/android/gradlew
new file mode 100755
index 00000000..4f906e0c
--- /dev/null
+++ b/android/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
new file mode 100644
index 00000000..ac1b06f9
--- /dev/null
+++ b/android/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
new file mode 100644
index 00000000..79b7af40
--- /dev/null
+++ b/android/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ maven { url = uri("https://jitpack.io") }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ maven { url = uri("https://jitpack.io") }
+ maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") }
+ mavenCentral()
+ }
+}
+
+rootProject.name = "customfetch_android"
+include(":app")
diff --git a/compile_flags.txt b/compile_flags.txt
index 4da4b886..593d7218 100644
--- a/compile_flags.txt
+++ b/compile_flags.txt
@@ -8,6 +8,10 @@
-DGUI_MODE=1
-DUSE_DCONF=1
-DDEBUG=1
+-DANDROID_APP=1
+-DENABLE_NLS=1
+-DLOCALEDIR="/usr/share/locale"
+-I./useless_stuff/
-I/usr/include/gtkmm-3.0
-I/usr/lib/gtkmm-3.0/include
-I/usr/include/gtk-3.0
diff --git a/customfetch.1 b/customfetch.1
index f30d72c9..e9489cb4 100644
--- a/customfetch.1
+++ b/customfetch.1
@@ -108,22 +108,19 @@ e.g "the number 50 is \\< than 100 \\& 98"
.SH OPTIONS
.TP
-\fB\-n\fR, \fB\-\-no\-display\fR
+\fB\-n\fR, \fB\-\-no\-logo\fR []
Do not display the logo
.TP
-\fB\-N\fR, \fB\-\-no\-color\fR
+\fB\-N\fR, \fB\-\-no\-color\fR []
Do not output and parse colors. Useful for stdout or pipe operations
.TP
-\fB\-\-enable\-colors\fR
-Inverse of --no-color
-.TP
\fB\-s\fR, \fB\-\-source\-path\fR
Path to the ascii art file to display
.TP
\fB\-C\fR, \fB\-\-config\fR
Path to the config file to use
.TP
-\fB\-a\fR, \fB\-\-ascii-logo-type\fR []
+\fB\-a\fR, \fB\-\-ascii-logo-type\fR []
The type of ASCII art to apply ("small" or "old").
.br
Basically will add "_" to the logo filename.
@@ -148,7 +145,7 @@ An example: "[Liberation Mono] [Normal] [12]", which can be "Liberation Mono Nor
.br
It's recommended to use GUI mode for the moment if something doesn't work
.TP
-\fB\-m\fR, \fB\-\-layout\-line\fR
+\fB\-m\fR, \fB\-\-layout\-line\fR
Will replace the config layout, with a layout you specify in the arguments
.br
Example: "customfetch -m "${auto}OS: $" -m "${auto}CPU: $" "
@@ -159,7 +156,7 @@ Will only print the logo (if not disabled), along side the parsed OS and CPU
Use GUI mode instead of priting in the terminal (use \fB\-V\fR to check if it's enabled)
.TP
\fB\-p\fR, \fB\-\-logo-position\fR
-Position of the logo ("top" or "left")
+Position of the logo ("top" or "left" or "bottom")
.TP
\fB\-o\fR, \fB\-\-offset\fR
Offset between the ascii art and the system infos
@@ -170,7 +167,7 @@ Print the list of the tags and its members
\fB\-h\fR, \fB\-\-help\fR
Print this help menu
.TP
-\fB\-L\fR, \fB\-\-logo\-only\fR
+\fB\-L\fR, \fB\-\-logo\-only\fR []
Print only the logo
.TP
\fB\-V\fR, \fB\-\-version\fR
@@ -179,8 +176,8 @@ Print the version along with the git branch it was built
\fB\-\-bg\-image\fR
Path to image to be used in the background in GUI (put "disable" for disabling in the config)
.TP
-\fB\-\-wrap\-lines\fR [<0,1>]
-Disable (0) or Enable (1) wrapping lines when printing in terminal
+\fB\-\-wrap\-lines\fR []
+Wrap lines when printing in terminal
.TP
\fB\-\-logo\-padding\-top\fR
Padding of the logo from the top
@@ -188,21 +185,24 @@ Padding of the logo from the top
\fB\-\-logo\-padding\-left\fR
Padding of the logo from the left
.TP
+\fB\-\-layout\-padding\-top\fR
+Padding of the layout from the top
+.TP
\fB\-\-title\-sep\fR
-A char (or string) to use in $
+A character (or string) to use in $
.TP
\fB\-\-sep\-reset\fR
-A separator (or string) that when encountered, will automatically reset color
+A character (or string) that when encountered, will automatically reset color
.TP
-\fB\-\-sep\-reset\-after\fR []
-Reset color either before of after 'sep-reset' (1 = after && 0 = before)
+\fB\-\-sep\-reset\-after\fR []
+Reset color either before or after 'sep-reset'
.TP
\fB\-\-gen\-config\fR []
Generate default config file to config folder (if path, it will generate to the path)
.br
Will ask for confirmation if file exists already
.TP
-\fB\-\-color\fR
+\fB\-\-add\-color\fR
Replace instances of a color with another value.
.br
Syntax MUST be "name=value" with no space between "=", example: --color "foo=#444333".
diff --git a/examples/config_cool.toml b/examples/config_cool.toml
index 4ee264b1..5bc208ea 100644
--- a/examples/config_cool.toml
+++ b/examples/config_cool.toml
@@ -189,9 +189,9 @@ use-SI-byte-unit = false
percentage-colors = ["green", "yellow", "red"]
# Usually in neofetch/fastfetch, when your terminal size is too small,
-# to render some text in 1 line, they wrap those lines.
+# to render some text in 1 line, they don't wrap those lines, instead they truncate them.
# Enable/Disable if you want this
-wrap-lines = true
+wrap-lines = false
# $ config
[os.uptime]
diff --git a/examples/config_simple.toml b/examples/config_simple.toml
index 7bf95fea..6d125ce4 100644
--- a/examples/config_simple.toml
+++ b/examples/config_simple.toml
@@ -170,9 +170,9 @@ use-SI-byte-unit = false
percentage-colors = ["green", "yellow", "red"]
# Usually in neofetch/fastfetch, when your terminal size is too small,
-# to render some text in 1 line, they wrap those lines.
+# to render some text in 1 line, they don't wrap those lines, instead they truncate them.
# Enable/Disable if you want this
-wrap-lines = true
+wrap-lines = false
# $ config
[os.uptime]
diff --git a/examples/nitch_like_config.toml b/examples/nitch_like_config.toml
index d06d369f..2573bd86 100644
--- a/examples/nitch_like_config.toml
+++ b/examples/nitch_like_config.toml
@@ -171,9 +171,9 @@ use-SI-byte-unit = false
percentage-colors = ["green", "yellow", "red"]
# Usually in neofetch/fastfetch, when your terminal size is too small,
-# to render some text in 1 line, they wrap those lines.
+# to render some text in 1 line, they don't wrap those lines, instead they truncate them.
# Enable/Disable if you want this
-wrap-lines = true
+wrap-lines = false
# $ config
[os.uptime]
diff --git a/include/config.hpp b/include/config.hpp
index cd6f4d1b..d52f888e 100644
--- a/include/config.hpp
+++ b/include/config.hpp
@@ -33,6 +33,8 @@
#include "toml++/toml.hpp"
#include "util.hpp"
+// config colors
+// those without gui_ prefix are for the terminal
struct colors_t
{
std::string black;
@@ -57,9 +59,10 @@ struct colors_t
class Config
{
public:
- Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors);
+ // Create .config directories and files and load the config file (args or default)
+ Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors, bool do_not_load = false);
- // config file
+ // Variables of config file in [config] table
std::vector layout;
std::vector percentage_colors;
std::vector colors_name, colors_value;
@@ -81,12 +84,14 @@ class Config
bool use_SI_unit = false;
bool wrap_lines = false;
- // modules specific config
+ // Variables of config file for
+ // modules specific configs, e.g [uptime]
std::string uptime_d_fmt;
std::string uptime_h_fmt;
std::string uptime_m_fmt;
std::string uptime_s_fmt;
+ // [pkgs]
std::vector pkgs_managers;
std::vector pacman_dirs;
std::vector flatpak_dirs;
@@ -102,12 +107,37 @@ class Config
bool m_display_distro = true;
bool m_print_logo_only = false;
- void loadConfigFile(const std::string_view filename, colors_t& colors);
- std::string getThemeValue(const std::string_view value, const std::string_view fallback) const;
- void generateConfig(const std::string_view filename);
- void addAliasColors(const std::string& str);
- std::vector getValueArrayStr(const std::string_view value, const std::vector& fallback);
+ /**
+ * Load config file and parse every config variables
+ * @param filename The config file path
+ * @param colors The colors struct where we'll put the default config colors.
+ * It doesn't include the colors in config.alias-colors
+ */
+ void loadConfigFile(const std::string_view filename, colors_t& colors);
+
+ /**
+ * Generate a config file
+ * @param filename The config file path
+ */
+ void generateConfig(const std::string_view filename);
+
+ /**
+ * Add alias values to colors_name and colors_value.
+ * @param str The alias color to add.
+ * Must have a '=' for separating color name and value,
+ * E.g "pink=!#FFC0CB"
+ */
+ void addAliasColors(const std::string& str);
+
+private:
+ // Parsed config from loadConfigFile()
+ toml::table tbl;
+ /**
+ * Get value of config variables
+ * @param value The config variable "path" (e.g "config.source-path")
+ * @param fallback Default value if couldn't retrive value
+ */
template
T getValue(const std::string_view value, const T&& fallback) const
{
@@ -118,10 +148,23 @@ class Config
return ret.value_or(fallback);
}
-private:
- toml::table tbl;
+ /**
+ * getValue() but don't want to specify the template, so it's std::string,
+ * and because of the name, only used when retriving the colors for terminal and GUI
+ * @param value The config variable "path" (e.g "config.gui-red")
+ * @param fallback Default value if couldn't retrive value
+ */
+ std::string getThemeValue(const std::string_view value, const std::string_view fallback) const;
+
+ /**
+ * Get value of config array of string variables
+ * @param value The config variable "path" (e.g "config.gui-red")
+ * @param fallback Default value if couldn't retrive value
+ */
+ std::vector getValueArrayStr(const std::string_view value, const std::vector& fallback);
};
+// default config
inline constexpr std::string_view AUTOCONFIG = R"#([config]
# customfetch is designed with customizability in mind
# here is how it works:
@@ -157,7 +200,7 @@ inline constexpr std::string_view AUTOCONFIG = R"#([config]
# The colors can be predefined such as: black, red, green, blue, cyan, yellow, magenta, white.
# They can be configured in the config file.
#
-# They can have hexcodes colors (e.g "#5522dd").
+# They can have hex codes colors (e.g "#5522dd").
# You can apply special effects to colors by using the following symbols before the '#' in hex codes:
#
# Terminal and GUI GUI Only
@@ -197,8 +240,8 @@ inline constexpr std::string_view AUTOCONFIG = R"#([config]
#
# Read the manual customfetch.1 for more infos with $() tag
#
-# Q: Can I run recursive tags?
-# A: If "$),23]).mountdir>).disk>" works,
+# Q: Can I use recursive tags?
+# A: If "$),23]).mountdir>)>" works,
# Then I guess yeah
################################################################
@@ -211,14 +254,22 @@ layout = [
"${auto}Uptime: $",
"${auto}Terminal: $",
"${auto}Shell: $",
- "${auto}Packages: $",
+ "${auto}Packages: $",)#"
+#if !ANDROID_APP
+ R"#(
"${auto}Theme: $",
"${auto}Icons: $",
"${auto}Font: $",
"${auto}Cursor: $",
"${auto}WM: $",
"${auto}DE: $",
+ "${auto}Disk (/): $",)#"
+#else
+ R"#(
"${auto}Disk (/): $",
+ "${auto}Disk (/sdcard): $",)#"
+#endif
+ R"#(
"${auto}Swap: $",
"${auto}CPU: $",
"${auto}GPU: $",
@@ -236,18 +287,30 @@ source-path = "os"
# Path to where we'll take all the distros/OSs ascii arts.
# note: it MUST contain an "ascii" subdirectory
-data-dir = "/usr/share/customfetch"
+)#"
+#if !ANDROID_APP
+R"#(data-dir = "/usr/share/customfetch")#"
+#else
+R"#(data-dir = "/data/user/0/org.toni.customfetch_android/files")#"
+#endif
+R"#(
# The type of ASCII art to apply ("small", "old").
# Basically will add "_" to the logo filename.
# It will return the regular linux ascii art if it doesn't exist.
# Leave empty it for regular.
-ascii-logo-type = ""
+)#"
+#if !ANDROID_APP
+R"#(ascii-logo-type = "")#"
+#else
+R"#(ascii-logo-type = "small")#"
+#endif
+R"#(
# A char (or string) to use in $
title-sep = "-"
-# A separator (or string) that when ecountered, will automatically
+# A separator (or string) that when encountered, will automatically
# reset color, aka. automatically add ${0} (only in layout)
# Make it empty for disabling
sep-reset = ":"
@@ -258,7 +321,7 @@ sep-reset = ":"
sep-reset-after = false
# Where the logo should be displayed.
-# Values: "top" or "left"
+# Values: "top" or "left" or "bottom"
logo-position = "left"
# Offset between the ascii art and the layout
@@ -283,9 +346,9 @@ magenta = "\e[1;35m"
cyan = "\e[1;36m"
white = "\e[1;37m"
-# Alias colors. Basically more color variables, but config depending (no shot).
+# Alias colors. Basically more color variables.
# They can be used as like as the color tag.
-# This is as like as using the --color argument
+# This is as like as using the --add-color argument
# Syntax must be "name=value", e.g "purple=magenta" or "orange=!#F08000"
alias-colors = ["purple=magenta"]
@@ -297,16 +360,16 @@ use-SI-byte-unit = false
# Colors to be used in percentage tag and modules members.
# They are used as if you're using the color tag.
-# It's an array just for "convinience"
+# It's an array just for "convenience"
# 1st color for good
# 2nd color for normal
# 3rd color for bad
percentage-colors = ["green", "yellow", "red"]
# Usually in neofetch/fastfetch, when your terminal size is too small,
-# to render some text in 1 line, they wrap those lines.
+# to render some text in 1 line, they don't wrap those lines, instead they truncate them.
# Enable/Disable if you want this
-wrap-lines = true
+wrap-lines = false
# Warn against tradeoffs between slower queries for availability
# e.g. falling back to gsettings when we can't find the config file for GTK
@@ -327,18 +390,18 @@ secs = " seconds"
# remember to not enter the same name twice, else the world will finish
# Choices: pacman, flatpak, dpkg, apk
#
-# Pro-tip: if your package manager isnt listed here, yet,
+# Pro-tip: if your package manager isn't listed here, yet,
# use the bash command tag in the layout
# e.g "Packages: $(pacman -Q | wc -l) (pacman)"
pkg-managers = ["pacman", "dpkg", "flatpak"]
# Distros and package manager specific
# package manager paths for getting the packages count from path.
-# They are arrayies so you can add multiple paths.
+# They are arrays so you can add multiple paths.
#
# If you don't know what these ares, leave them by default settings
pacman-dirs = ["/var/lib/pacman/local/"]
-dpkg-files = ["/var/lib/dpkg/status"]
+dpkg-files = ["/var/lib/dpkg/status", "/data/data/com.termux/files/usr/var/lib/dpkg/status"]
flatpak-dirs = ["/var/lib/flatpak/app/", "~/.local/share/flatpak/app/"]
apk-files = ["/var/lib/apk/db/installed"]
diff --git a/include/display.hpp b/include/display.hpp
index de92a443..ba702795 100644
--- a/include/display.hpp
+++ b/include/display.hpp
@@ -35,7 +35,7 @@ namespace Display
{
/*
- * Render the layout along side the ASCII art and return the vector
+ * Render the layout along side the source file and return the vector
* @param config The config class
* @param colors The colors
* @param already_analyzed_path If already checked that the source path is not a binary file
@@ -45,7 +45,7 @@ std::vector render(const Config& config, const colors_t& colors, co
const std::string_view path);
/*
- * Display the ascii art and layout
+ * Display the rendered result (or just display a normal vector of strings)
* @param renderResult The rendered vector usually by Display::render()
*/
void display(const std::vector& renderResult);
diff --git a/include/gui.hpp b/include/gui.hpp
index b401ee18..c43df01a 100644
--- a/include/gui.hpp
+++ b/include/gui.hpp
@@ -26,7 +26,7 @@
#ifndef _GUI_HPP
#define _GUI_HPP
-#ifdef GUI_MODE
+#if GUI_MODE && !ANDROID_APP
#include "config.hpp"
#include "gdkmm/pixbuf.h"
@@ -45,7 +45,15 @@ namespace GUI
class Window : public Gtk::Window
{
public:
+
+ /**
+ * Initialize and create everything and parse layout with source path.
+ * @param config The config class
+ * @param colors The non-alias colors struct
+ * @param path The logo source path
+ */
Window(const Config& config, const colors_t& colors, const std::string_view path);
+ // Destroy the window, handled by GTK
virtual ~Window();
private:
@@ -59,19 +67,22 @@ class Window : public Gtk::Window
Glib::RefPtr m_bg_static_image;
int m_width, m_height;
- void on_window_resize(Gtk::Allocation& allocation)
+ // Update background image size (gif or static)
+ // on window resize
+ void on_window_resize(const Gtk::Allocation& allocation)
{
m_width = allocation.get_width();
m_height = allocation.get_height();
if (m_bg_static_image)
- // static image: Update to fit the new window size
+ // static image: update to fit the new window size
update_static_image();
else if (m_iter)
- // animated image: Update the current frame
+ // gif: update the current frame
update_frame();
}
+ // Update background gif size
bool on_update_animation()
{
if (!m_iter)
@@ -83,6 +94,7 @@ class Window : public Gtk::Window
return true; // continue the timer
}
+ // Update background image size
void update_static_image()
{
// scale the static image to fit the window size
@@ -90,6 +102,7 @@ class Window : public Gtk::Window
m_bg_image.set(scaled_image);
}
+ // Update background gif size in the current frame
void update_frame()
{
// scale the current frame of the animation to fit the window size
@@ -105,6 +118,6 @@ class Window : public Gtk::Window
} // namespace GUI
-#endif // GUI_MODE
+#endif // GUI_MODE && !ANDROID_APP
#endif // _GUI_HPP
diff --git a/include/parse.hpp b/include/parse.hpp
index 8d12d11b..e9e549a1 100644
--- a/include/parse.hpp
+++ b/include/parse.hpp
@@ -26,11 +26,12 @@
#ifndef _PARSE_HPP
#define _PARSE_HPP
+#include
#include "config.hpp"
#include "query.hpp"
-/* the additional args that parse() needs for getting the necessary infos/configs.
- * only used for making the argument passing more clear.
+/* The additional args that parse() needs for getting the necessary infos/configs.
+ * Only used for making the argument passing more clear.
* Always pass it non-const and by reference
*/
struct parse_args_t
@@ -40,7 +41,9 @@ struct parse_args_t
const Config& config;
const colors_t& colors;
bool parsingLayout;
- bool firstrun_clr;
+ bool firstrun_clr = true;
+ bool no_more_reset = false;
+ std::string endspan; // only if ANDROID_APP
};
/* Parse input, in-place, with data from systemInfo.
@@ -54,7 +57,7 @@ struct parse_args_t
* @param is_image If the source path is an image (used for GUI mode only)
*/
std::string parse(std::string input, systemInfo_t& systemInfo, std::string& pureOutput, const Config& config,
- const colors_t& colors, const bool parsingLayout);
+ const colors_t& colors, const bool parsingLayout, bool& no_more_reset);
// parse() for parse_args_t& arguments
std::string parse(const std::string_view input, parse_args_t& parse_args);
@@ -81,6 +84,9 @@ void addValueFromModule(const std::string& moduleName, parse_args_t& parse_args)
/*
* Return a module member value
+ * @param systemInfo The systemInfo_t map
+ * @param moduleName The module name
+ * @param moduleMemberName The module member name
*/
std::string getInfoFromName(const systemInfo_t& systemInfo, const std::string_view moduleName,
const std::string_view moduleMemberName);
@@ -92,4 +98,6 @@ void append_styles(fmt::text_style& current_style, Styles&&... styles)
current_style |= (styles | ...);
}
+inline std::vector auto_colors;
+
#endif
diff --git a/include/pci.ids.hpp b/include/pci.ids.hpp
index 204e65eb..083c6be7 100644
--- a/include/pci.ids.hpp
+++ b/include/pci.ids.hpp
@@ -1,6 +1,9 @@
#ifndef _PCI_IDS_HPP
#define _PCI_IDS_HPP
+#include "platform.hpp"
+#if !CF_ANDROID
+
#include
#include
@@ -39088,4 +39091,12 @@ inline const std::string& all_ids =
inline constexpr std::array pci_vendors_array = get_pci_vendors_array();
inline constexpr std::array pci_vendors_location_array = get_pci_vendors_location_array();
-#endif // _PCI_IDS_HPP
\ No newline at end of file
+#else
+
+inline const std::string& all_ids = {};
+inline constexpr std::array pci_vendors_array = {};
+inline constexpr std::array pci_vendors_location_array = {};
+
+#endif // !CF_ANDROID
+
+#endif // _PCI_IDS_HPP
diff --git a/include/platform.hpp b/include/platform.hpp
new file mode 100644
index 00000000..a6226f73
--- /dev/null
+++ b/include/platform.hpp
@@ -0,0 +1,26 @@
+#ifndef _PLATFORM_H
+#define _PLATFORM_H
+
+#if (defined(__ANDROID__) || defined(ANDROID_API))
+# define CF_ANDROID 1
+#else
+# define CF_ANDROID 0
+#endif
+
+#if (defined(unix) || defined(__unix) || defined(__unix__)) && !CF_ANDROID
+# define CF_UNIX 1
+#else
+# define CF_UNIX 0
+#endif
+
+#if (defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__))
+# define CF_WINDOWS 1
+#else
+# define CF_WINDOWS 0
+#endif
+
+#if !(CF_UNIX || CF_ANDROID) || CF_WINDOWS
+# error "Platform currently not supported, only Unix and Android"
+#endif
+
+#endif // _PLATFORM_H
diff --git a/include/query.hpp b/include/query.hpp
index eb4b1625..42abd395 100644
--- a/include/query.hpp
+++ b/include/query.hpp
@@ -31,7 +31,6 @@
#include
#include
#include
-#include
#include "config.hpp"
#include "util.hpp"
@@ -46,8 +45,10 @@ extern "C" {
#include
}
+// Special variable for storing info modules values
using systemInfo_t =
std::unordered_map>>;
+// used in systemInfo_t most of the time
using variant = std::variant;
namespace Query
@@ -107,7 +108,8 @@ class User
public:
struct User_t
{
- std::string shell_name{ UNKNOWN };
+ std::string shell_path{ MAGIC_LINE };
+ std::string shell_name{ MAGIC_LINE };
std::string shell_version{ UNKNOWN };
std::string wm_name{ MAGIC_LINE };
std::string wm_version{ UNKNOWN };
@@ -152,23 +154,21 @@ class Theme
std::string cursor_size{ UNKNOWN };
};
- Theme(const std::uint8_t ver, systemInfo_t& queried_themes, std::vector& queried_themes_names,
- const std::string& theme_name_version, const Config& config, const bool gsettings_only = false);
+ Theme(const std::uint8_t ver, systemInfo_t& queried_themes, const std::string& theme_name_version,
+ const Config& config, const bool gsettings_only = false);
- Theme(systemInfo_t& queried_themes, const Config& config, const bool gsettings_only = false);
+ Theme(const Config& config, const bool gsettings_only = false);
- std::string gtk_theme() noexcept;
- std::string gtk_icon_theme() noexcept;
- std::string gtk_font() noexcept;
+ std::string& gtk_theme() noexcept;
+ std::string& gtk_icon_theme() noexcept;
+ std::string& gtk_font() noexcept;
std::string& cursor() noexcept;
std::string& cursor_size() noexcept;
private:
- User query_user;
- static Theme_t m_theme_infos;
- systemInfo_t& m_queried_themes;
- const std::string m_theme_name_version;
- std::string m_wmde_name;
+ User query_user;
+ std::string m_wmde_name;
+ static Theme_t m_theme_infos;
};
class CPU
@@ -187,12 +187,19 @@ class CPU
// private:
double freq_max_cpuinfo = 0;
+ std::string modelname;
+ std::string vendor;
};
CPU() noexcept;
std::string& name() noexcept;
std::string& nproc() noexcept;
+
+ // only in Android
+ std::string& vendor() noexcept;
+ std::string& modelname() noexcept;
+
double& freq_max() noexcept;
double& freq_min() noexcept;
double& freq_cur() noexcept;
@@ -213,18 +220,18 @@ class GPU
std::string vendor{ UNKNOWN };
};
- GPU(const std::uint16_t id, std::vector& queried_gpus);
+ GPU(const std::string& id, systemInfo_t& queried_gpus);
std::string& name() noexcept;
std::string& vendor() noexcept;
private:
- uint16_t m_vendor_id;
- uint16_t m_device_id;
- std::string m_vendor_id_s;
- std::string m_device_id_s;
+ uint16_t m_vendor_id;
+ uint16_t m_device_id;
+ std::string m_vendor_id_s;
+ std::string m_device_id_s;
- static GPU_t m_gpu_infos;
+ static GPU_t m_gpu_infos;
};
class Disk
@@ -240,7 +247,7 @@ class Disk
std::string mountdir;
};
- Disk(const std::string_view path, std::vector& paths);
+ Disk(const std::string& path, systemInfo_t& queried_paths);
double& total_amount() noexcept;
double& free_amount() noexcept;
diff --git a/include/switch_fnv1a.hpp b/include/switch_fnv1a.hpp
index 9d1266b8..2d44ea57 100644
--- a/include/switch_fnv1a.hpp
+++ b/include/switch_fnv1a.hpp
@@ -52,21 +52,6 @@ struct fnv1a_traits<64>
static constexpr Type Offset = 0xcbf29ce484222325;
};
-static constexpr __uint128_t Pack128(uint64_t high, uint64_t low)
-{
- return ((__uint128_t)high << 64) + (__uint128_t)low;
-}
-
-// Traits for 128-bit FNV1a
-template<>
-struct fnv1a_traits<128>
-{
- static constexpr bool Supported = true;
- using Type = __uint128_t;
-
- static constexpr Type Prime = Pack128(0x1000000, 0x000000000000013b);
- static constexpr Type Offset = Pack128(0x6c62272e07bb0142, 0x62b821756295c58d);
-};
// Generic FNV1a implementation
template
@@ -176,7 +161,8 @@ struct fnv1a
using fnv1a16 = fnv1a<16>;
using fnv1a32 = fnv1a<32>;
using fnv1a64 = fnv1a<64>;
-using fnv1a128 = fnv1a<128>;
+// we ain't gonna use this, so change to 16 bits instead
+using fnv1a128 = fnv1a<16>;
constexpr fnv1a16::Type operator"" _fnv1a16(const char* s, const std::size_t l)
{
diff --git a/include/util.hpp b/include/util.hpp
index b53f9db8..2f1093aa 100644
--- a/include/util.hpp
+++ b/include/util.hpp
@@ -29,31 +29,52 @@
#include
#include
+#include
#include
+#include
#include
#include
+#include "fmt/chrono.h"
#include "fmt/color.h"
-#include "fmt/core.h"
+#include "fmt/base.h"
+#include "fmt/os.h"
+#include "platform.hpp"
// clang-format off
+// Get string literal length
constexpr std::size_t operator""_len(const char*, std::size_t ln) noexcept
{
return ln;
}
+// used for auto_devide_bytes()
struct byte_units_t
{
std::string unit;
double num_bytes;
};
+#if ENABLE_NLS
+/* here so it doesn't need to be included elsewhere */
+#include
+#include
+#define _(str) gettext(str)
+#else
+#define _(s) (char*)s
+#endif
+
constexpr const char NOCOLOR[] = "\033[0m";
constexpr const char NOCOLOR_BOLD[] = "\033[0m\033[1m";
constexpr const char UNKNOWN[] = "(unknown)";
-// magic line to be sure that I don't cut the wrong line
-constexpr const char MAGIC_LINE[] = "(cut this shit NOW!! RAHHH)";
+// Usually in neofetch/fastfetch when some infos couldn't be queried,
+// they remove it from the display. With customfetch is kinda difficult to know when to remove
+// the info to display, since it's all modular with tags, so I have created
+// magic line to be sure that I don't cut the wrong line.
+//
+// Every instance of this string in a layout line, the whole line will be erased.
+constexpr const char MAGIC_LINE[] = "(cut this line NOW!! RAHHH)";
/* lib = library to load (string)
* code = code to execute if anything goes wrong
@@ -75,37 +96,214 @@ constexpr const char MAGIC_LINE[] = "(cut this shit NOW!! RAHHH)";
#define BOLD_COLOR(x) (fmt::emphasis::bold | fmt::fg(x))
-bool hasEnding(const std::string_view fullString, const std::string_view ending);
-bool hasStart(const std::string_view fullString, const std::string_view start);
-std::string name_from_entry(size_t dev_entry_pos);
-std::string vendor_from_entry(size_t vendor_entry_pos, const std::string_view vendor_id);
-std::string binarySearchPCIArray(const std::string_view vendor_id, const std::string_view pci_id);
-std::string binarySearchPCIArray(const std::string_view vendor_id);
-std::string read_shell_exec(const std::string_view cmd);
-void getFileValue(u_short& iterIndex, const std::string_view line, std::string& str, const size_t& amount);
+/* https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c#874160
+ * Check if substring exists at the end
+ * @param fullString The string to lookup
+ * @param ending The string to check at the end of fullString
+ */
+bool hasEnding(const std::string_view fullString, const std::string_view ending);
+
+/* https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c#874160
+ * Check if substring exists at the start
+ * @param fullString The string to lookup
+ * @param start The string to check at the start of fullString
+ */
+bool hasStart(const std::string_view fullString, const std::string_view start);
+
+std::vector split(const std::string_view text, char delim);
+
+/* Get device name from `all_ids` in pci.ids.hpp
+ * @param dev_entry_pos Line position from where the device is located
+ */
+std::string name_from_entry(size_t dev_entry_pos);
+
+/* Get vendor device name from `all_ids` in pci.ids.hpp
+ * @param vendor_entry_pos Line position from where the device is located
+ * @param vendor_id_s The vendor ID (e.g 10de)
+ */
+std::string vendor_from_entry(const size_t vendor_entry_pos, const std::string_view vendor_id_s);
+
+/* Function to perform binary search on the pci vendors array to find a device from a vendor.
+ * @param vendor_id_s The vendor ID (e.g 10de)
+ * @param pci_id_s The device ID (e.g 1f82)
+ */
+std::string binarySearchPCIArray(const std::string_view vendor_id_s, const std::string_view pci_id_s);
+
+/* Function to perform binary search on the pci vendors array to find a vendor.
+ * @param vendor_id_s The vendor ID (e.g 10de)
+ */
+std::string binarySearchPCIArray(const std::string_view vendor_id_s);
+
+/* http://stackoverflow.com/questions/478898/ddg#478960
+ * Execute shell command and read its output from stdout.
+ * @param cmd The command to execute
+ */
+std::string read_shell_exec(const std::string_view cmd);
+
+/* Get file value from a file and trim quotes and double-quotes
+ * @param iterIndex The iteration index used for getting the necessary value only tot times
+ * @param line The string used in std::getline
+ * @param str The string to assign the trimmed value, inline
+ * @param amount The amount to be used in the line.substr() (should be used with something like "foobar"_len)
+ */
+void getFileValue(u_short& iterIndex, const std::string_view line, std::string& str, const size_t& amount);
+
+/* Covert bytes (or bibytes) to be accurate to the max prefix (maxprefix or YB/YiB)
+ * @param num The number to convert
+ * @param base Base to devide (1000 = bytes OR 1024 = bibytes)
+ * @param maxprefix The maxinum prefix we can go up to (empty for ignore)
+ */
byte_units_t auto_devide_bytes(const double num, const std::uint16_t base, const std::string_view maxprefix = "");
+
+/* Covert bytes (or bibytes) to be accurate to a specific prefix
+ * @param num The number to convert
+ * @param prefix The prefix we want to convert to (GiB, MB, etc.)
+ */
byte_units_t devide_bytes(const double num, const std::string_view prefix);
-bool is_file_image(const unsigned char* bytes);
-void ctrl_d_handler(const std::istream& cin);
-std::string expandVar(std::string ret);
-bool taur_exec(const std::vector cmd_str, const bool noerror_print = true);
-std::string which(const std::string_view command);
-std::string get_data_path(const std::string_view file);
-std::string get_data_dir(const std::string_view dir);
-std::string get_relative_path(const std::string_view relative_path, const std::string_view _env, const long long mode);
-bool read_binary_file(std::ifstream& f, std::string& ret);
-void replace_str(std::string& str, const std::string_view from, const std::string_view to);
-bool read_exec(std::vector cmd, std::string& output, bool useStdErr = false, bool noerror_print = true);
-std::string str_tolower(std::string str);
-std::string str_toupper(std::string str);
-void strip(std::string& input);
-std::string read_by_syspath(const std::string_view path);
-fmt::rgb hexStringToColor(const std::string_view hexstr);
-std::string shorten_vendor_name(std::string vendor);
-std::string getHomeConfigDir();
-std::string getConfigDir();
-std::vector split(const std::string_view text, char delim);
+/* Check if file is image (static or gif).
+ * Doesn't check for mp4, mp3 or other binary formats
+ * @param bytes The header bytes of the file
+ */
+bool is_file_image(const unsigned char* bytes);
+
+/* Write error message and exit if EOF (or CTRL-D most of the time)
+ * @param cin The std::cin used for getting the input
+ */
+void ctrl_d_handler(const std::istream& cin);
+
+/* Replace special symbols such as ~ and $ (at the begging) in std::string
+ * @param str The string
+ * @return The modified string
+ */
+std::string expandVar(std::string ret);
+
+/* Executes commands with execvp() and keep the program running without existing
+ * @param cmd_str The command to execute
+ * @param exitOnFailure Whether to call exit(1) on command failure.
+ * @return true if the command successed, else false
+ */
+bool taur_exec(const std::vector cmd_str, const bool noerror_print = true);
+
+/* Get a relative path from an enviroment variable (PATH, XDG_DATA_DIRS, ...)
+ * Either path of an executable, directory, etc...
+ * @param relative_path The path we would search in the env
+ * @param env The enviroment variable without $
+ * @param mode Mode of the file/directory using the enums declared in sys/stat.h
+ * Such as S_IXUSR for executables, S_IFDIR for directories, etc.
+ */
+std::string get_relative_path(const std::string_view relative_path, const std::string_view env, const long long mode);
+
+/* Simulate behaviour of the command `which`
+ * @param command The command to lookup in the $PATH
+ */
+std::string which(const std::string_view command);
+
+/* Get file path from $XDG_DATA_DIRS
+ * @param file The file to lookup in the env
+ */
+std::string get_data_path(const std::string_view file);
+
+/* Get directory path from $XDG_DATA_DIRS
+ * @param dir The directory to lookup in the env
+ */
+std::string get_data_dir(const std::string_view dir);
+
+/* Read a binary file and get its current line,
+ * which simulates the behaviour of the command `strings` but one line at the time
+ * @param f The std::ifstream of the file
+ * @param ret The string to return after we read the line
+ * @example
+ * std::ifstream f(exec_path, std::ios::binary);
+ * if (!f.is_open())
+ * return false;
+ *
+ * std::string line;
+ * while (read_binary_file(f, line))
+ * {
+ * if (line == "using GTK+-%d.%d.%d.")
+ * return true;
+ *
+ * // previous line, which will eventually be the version
+ * ret = line;
+ * }
+ * return false;
+ */
+bool read_binary_file(std::ifstream& f, std::string& ret);
+
+/* https://gist.github.com/GenesisFR/cceaf433d5b42dcdddecdddee0657292
+ * Replace every instances (inplace) of a substring
+ * @param str The full string to use
+ * @param from The substring to lookup to be replaced
+ * @param to The substring to replace in instances of `from`
+ */
+void replace_str(std::string& str, const std::string_view from, const std::string_view to);
+
+/* Executes commands with execvp() and read its output
+ * either from stdout or stderr
+ * @param cmd_str The command to execute
+ * @param output The string to use for appending the output
+ * @param useStdErr Read from stderr instead of stdout
+ * @param noerror_print Print errors
+ * @return true if the command successed, else false
+ */
+bool read_exec(std::vector cmd, std::string& output, bool useStdErr = false, bool noerror_print = true);
+
+/* Make whole string lowercase
+ * @param str The string to use
+ */
+std::string str_tolower(std::string str);
+
+/* Make whole string uppercase
+ * @param str The string to use
+ */
+std::string str_toupper(std::string str);
+
+/* Remove all white spaces (' ', '\t', '\n') from string inplace!
+ * @param input The string to strip
+ * @original https://github.com/lfreist/hwinfo/blob/main/include/hwinfo/utils/stringutils.h#L50
+ */
+void strip(std::string& input);
+
+/* Read file content (usually from /sys)
+ * and return its first (and only) line
+ * @param path The path of the file to read
+ */
+std::string read_by_syspath(const std::string_view path);
+
+/* Convert hex color (#255224) to a fmt::rgb
+ * @param hexstr The hex color string (must have a '#' at the start)
+ */
+fmt::rgb hexStringToColor(const std::string_view hexstr);
+
+/* Abbreviate the vendors names
+ * @param vendor The vendor name
+ */
+std::string shorten_vendor_name(std::string vendor);
+
+/*
+ * Get the user config directory
+ * either from $XDG_CONFIG_HOME or from $HOME/.config/
+ * @return user's config directory
+ */
+std::string getHomeConfigDir();
+
+/*
+ * Get the customfetch config directory
+ * where we'll have "config.toml"
+ * from getHomeConfigDir()
+ * @return customfetch's config directory
+ */
+std::string getConfigDir();
+
+#if CF_ANDROID
+/* Get android property name such as "ro.product.marketname"
+ * @param name The property name
+ */
+std::string get_android_property(const std::string_view name);
+#endif
+
+#if !ANDROID_APP
template
void error(const std::string_view fmt, Args&&... args) noexcept
{
@@ -143,6 +341,89 @@ void info(const std::string_view fmt, Args&&... args) noexcept
fmt::print(BOLD_COLOR((fmt::rgb(fmt::color::cyan))), "INFO: {}\n",
fmt::format(fmt::runtime(fmt), std::forward(args)...));
}
+#else
+#include "jni.h"
+#include "android/log.h"
+
+inline struct jni_objects
+{
+ JNIEnv *env;
+ jobject obj;
+} jni_objs;
+
+
+template
+static void nativeAndFileLog(JNIEnv *env, int log_level, const std::string_view fmt, Args&&... args)
+{
+ const std::string& fmt_str = fmt::format(fmt::runtime(fmt), args...);
+ jstring jMessage = env->NewStringUTF(fmt_str.c_str());
+ const char *cMessage = env->GetStringUTFChars(jMessage, nullptr);
+
+ __android_log_print(log_level, "customfetch_android", "%s", cMessage);
+
+ env->ReleaseStringUTFChars(jMessage, cMessage);
+
+ auto f = fmt::output_file(getConfigDir() + "/log.txt", fmt::file::CREATE | fmt::file::APPEND | fmt::file::WRONLY);
+ auto now = std::chrono::system_clock::now();
+ f.print("[{:%H:%M:%S}] ", now);
+ switch(log_level)
+ {
+ case ANDROID_LOG_FATAL: f.print("FATAL: {}\n", fmt_str); break;
+ case ANDROID_LOG_ERROR: f.print("ERROR: {}\n", fmt_str); break;
+ case ANDROID_LOG_WARN: f.print("WARNING: {}\n", fmt_str); break;
+ case ANDROID_LOG_INFO: f.print("INFO: {}\n", fmt_str); break;
+ //case ANDROID_LOG_DEBUG: f.print("[DEBUG]: {}\n", fmt_str); break;
+ }
+}
+
+template
+static void writeToErrorLog(const bool fatal, const std::string_view fmt, Args&&... args)
+{
+ const std::string& fmt_str = fmt::format(fmt::runtime(fmt), args...);
+ const std::string_view title = fatal ? "FATAL" : "ERROR";
+ auto f = fmt::output_file(getConfigDir() + "/error_log.txt", fmt::file::CREATE | fmt::file::APPEND | fmt::file::RDWR);
+ auto lock = fmt::output_file(getConfigDir() + "/error.lock");
+ auto now = std::chrono::system_clock::now();
+ f.print("[{:%H:%M:%S}] {}: {}\n", now, title, fmt_str);
+ lock.print("{}: {}\n", title, fmt_str);
+}
+
+template
+void error(const std::string_view fmt, Args&&... args) noexcept
+{
+ nativeAndFileLog(jni_objs.env, ANDROID_LOG_ERROR, fmt, std::forward(args)...);
+ writeToErrorLog(false, fmt, std::forward(args)...);
+}
+
+template
+void die(const std::string_view fmt, Args&&... args) noexcept
+{
+ nativeAndFileLog(jni_objs.env, ANDROID_LOG_FATAL, fmt, std::forward(args)...);
+ writeToErrorLog(true, fmt, std::forward(args)...);
+ //exit(1);
+}
+
+template
+void debug(const std::string_view fmt, Args&&... args) noexcept
+{
+#if DEBUG
+ nativeAndFileLog(jni_objs.env, ANDROID_LOG_DEBUG, fmt, std::forward(args)...);
+#endif
+}
+
+template
+void warn(const std::string_view fmt, Args&&... args) noexcept
+{
+ nativeAndFileLog(jni_objs.env, ANDROID_LOG_WARN, fmt, std::forward(args)...);
+}
+
+template
+void info(const std::string_view fmt, Args&&... args) noexcept
+{
+ nativeAndFileLog(jni_objs.env, ANDROID_LOG_INFO, fmt, std::forward(args)...);
+}
+
+#endif // !ANDROID_APP
/** Ask the user a yes or no question.
* @param def The default result
diff --git a/po/customfetch.pot b/po/customfetch.pot
new file mode 100644
index 00000000..ac7f35e0
--- /dev/null
+++ b/po/customfetch.pot
@@ -0,0 +1,319 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the customfetch package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: customfetch 0.10.2\n"
+"Report-Msgid-Bugs-To: https://github.com/Toni500github/customfetch\n"
+"POT-Creation-Date: 2025-01-02 20:18+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/config.cpp:39
+#, c++-format
+msgid "customfetch config folder was not found, Creating folders at {}!"
+msgstr ""
+
+#: src/config.cpp:45
+#, c++-format
+msgid "config file {} not found, generating new one"
+msgstr ""
+
+#: src/config.cpp:60
+#, c++-format
+msgid ""
+"Parsing config file '{}' failed:\n"
+"{}\n"
+"\t(error occurred at line {} column {})"
+msgstr ""
+
+#: src/config.cpp:120
+msgid ""
+"the config array percentage-colors doesn't have 3 colors for being used in "
+"percentage tag and modules\n"
+"backing up to green, yellow and red"
+msgstr ""
+
+#: src/config.cpp:153
+#, c++-format
+msgid "An element of the '{}' array variable is not a string"
+msgstr ""
+
+#: src/config.cpp:167
+#, c++-format
+msgid ""
+"alias color '{}' does NOT have an equal sign '=' for separating color name "
+"and value.\n"
+"for more check with --help"
+msgstr ""
+
+#: src/display.cpp:95 src/gui.cpp:73
+#, c++-format
+msgid "Unable to load image '{}'"
+msgstr ""
+
+#: src/display.cpp:124
+#, c++-format
+msgid ""
+"The image backend '{}' isn't supported, only kitty and viu.\n"
+"Please currently use the GUI mode for rendering the image/gif (use -h for "
+"more details)"
+msgstr ""
+
+#: src/display.cpp:169
+msgid "getpos: error reading response!"
+msgstr ""
+
+#: src/display.cpp:213
+#, c++-format
+msgid "Could not open logo file '{}'"
+msgstr ""
+
+#: src/display.cpp:280
+msgid "images are NOT allowed in the android widget at the moment"
+msgstr ""
+
+#: src/gui.cpp:168
+#, c++-format
+msgid "Background image path '{}' doesn't exist"
+msgstr ""
+
+#: src/main.cpp:373
+#, c++-format
+msgid "config file '{}' doesn't exist"
+msgstr ""
+
+#: src/main.cpp:626
+#, c++-format
+msgid "'{}' doesn't exist. Can't load image/text file"
+msgstr ""
+
+#: src/main.cpp:638
+msgid ""
+"Can't run in GUI mode because it got disabled at compile time\n"
+"Compile customfetch with GUI_MODE=1 or contact your distro to enable it"
+msgstr ""
+
+#: src/parse.cpp:119
+#, c++-format
+msgid ""
+"Parser: failed to parse layout/ascii art: missing 'm' while using ANSI color "
+"escape code in '{}'"
+msgstr ""
+
+#: src/parse.cpp:176
+#, c++-format
+msgid ""
+"ANSI escape code color '\\e[{}' should have an rgb type value\n"
+"e.g \\e[38;2;255;255;255m"
+msgstr ""
+
+#: src/parse.cpp:180
+msgid ""
+"Parser: failed to parse layout/ascii art: missing m while using ANSI color "
+"escape code"
+msgstr ""
+
+#: src/parse.cpp:409
+#, c++-format
+msgid "{} mode in color {} doesn't have close bracket"
+msgstr ""
+
+#: src/parse.cpp:496 src/parse.cpp:776
+#, c++-format
+msgid "256 true color '{}' works only in terminal"
+msgstr ""
+
+#: src/parse.cpp:510 src/parse.cpp:604 src/parse.cpp:794
+#, c++-format
+msgid "PARSER: failed to parse line with color '{}'"
+msgstr ""
+
+#: src/parse.cpp:667
+#, c++-format
+msgid "{} mode in tag color {} doesn't have close bracket"
+msgstr ""
+
+#: src/parse.cpp:859
+#, c++-format
+msgid "percentage tag '{}' doesn't have a comma for separating the 2 numbers"
+msgstr ""
+
+#: src/parse.cpp:904
+#, c++-format
+msgid "PARSER: Missing tag close bracket {} in string '{}'"
+msgstr ""
+
+#: src/parse.cpp:1337
+#, c++-format
+msgid ""
+"seems theme-gtk module name '{}' doesn't have a version number to query.\n"
+"Syntax should be like 'theme_gtkN' which N stands for the version of gtk to "
+"query (single number)"
+msgstr ""
+
+#: src/parse.cpp:1411 src/parse.cpp:1669
+#, c++-format
+msgid "invalid disk module name '{}', must be disk(/path/to/fs) e.g: disk(/)"
+msgstr ""
+
+#: src/parse.cpp:1590 src/parse.cpp:1853
+#, c++-format
+msgid "Invalid module name: {}"
+msgstr ""
+
+#: src/parse.cpp:1812
+#, c++-format
+msgid ""
+"color palette module member '{}' in invalid.\n"
+"Must be used like 'colors_symbol(`symbol for printing the color palette`)'.\n"
+"e.g 'colors_symbol(@)' or 'colors_symbol(string)'"
+msgstr ""
+
+#: src/parse.cpp:1836
+#, c++-format
+msgid ""
+"light color palette module member '{}' in invalid.\n"
+"Must be used like 'colors_light_symbol(`symbol for printing the color "
+"palette`)'.\n"
+"e.g 'colors_light_symbol(@)' or 'colors_light_symbol(string)'"
+msgstr ""
+
+#: src/util.cpp:83
+msgid "Exiting due to CTRL-D or EOF"
+msgstr ""
+
+#: src/util.cpp:97
+msgid "FATAL: $HOME enviroment variable is not set (how?)"
+msgstr ""
+
+#: src/util.cpp:115
+#, c++-format
+msgid "No such enviroment variable: {}"
+msgstr ""
+
+#: src/util.cpp:130
+#, c++-format
+msgid "Failed to open {}"
+msgstr ""
+
+#: src/util.cpp:360
+#, c++-format
+msgid "pipe() failed: {}"
+msgstr ""
+
+#: src/util.cpp:389 src/util.cpp:449
+#, c++-format
+msgid "Failed to execute the command: {}"
+msgstr ""
+
+#: src/util.cpp:402
+#, c++-format
+msgid "An error has occurred with execvp: {}"
+msgstr ""
+
+#: src/util.cpp:408 src/util.cpp:427
+#, c++-format
+msgid "fork() failed: {}"
+msgstr ""
+
+#: src/util.cpp:437
+#, c++-format
+msgid "An error has occurred: {}: {}"
+msgstr ""
+
+#: src/util.cpp:562
+#, c++-format
+msgid "popen() failed: {}"
+msgstr ""
+
+#: src/util.cpp:630
+msgid "Failed to find $HOME, set it to your home directory!"
+msgstr ""
+
+#: src/query/unix/cpu.cpp:204
+#, c++-format
+msgid "Could not open {}"
+msgstr ""
+
+#: src/query/unix/disk.cpp:62
+msgid "setmntent"
+msgstr ""
+
+#: src/query/unix/disk.cpp:63
+msgid "setmntent() failed. Could not get disk info"
+msgstr ""
+
+#: src/query/unix/disk.cpp:85
+msgid "Failed to get disk info"
+msgstr ""
+
+#: src/query/unix/gpu.cpp:104
+msgid "Failed to parse GPU infos on the path /sys/class/drm/"
+msgstr ""
+
+#: src/query/unix/ram.cpp:59
+#, c++-format
+msgid ""
+"Could not open {}\n"
+"Failed to get RAM infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:98
+msgid "Failed to get OS infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:172
+#, c++-format
+msgid ""
+"uname() failed: {}\n"
+"Could not get system infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:175
+#, c++-format
+msgid ""
+"sysinfo() failed: {}\n"
+"Could not get system infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:236
+msgid "/proc/1/comm doesn't exist! (what?)"
+msgstr ""
+
+#: src/query/unix/theme.cpp:179 src/query/unix/theme.cpp:381
+msgid ""
+"customfetch could not detect a gtk configuration file. customfetch will use "
+"the much-slower gsettings."
+msgstr ""
+
+#: src/query/unix/theme.cpp:180 src/query/unix/theme.cpp:382
+msgid ""
+"If there's a file in a standard location that we aren't detecting, please "
+"file an issue on our GitHub."
+msgstr ""
+
+#: src/query/unix/theme.cpp:383
+msgid ""
+"You can disable this warning by disabling slow-query-warnings in your config."
+"toml file."
+msgstr ""
+
+#: src/query/unix/user.cpp:331
+#, c++-format
+msgid ""
+"getpwent failed: {}\n"
+"Could not get user infos"
+msgstr ""
+
+#: src/query/unix/utils/dewm.cpp:201
+msgid "Root node not found"
+msgstr ""
diff --git a/po/it_IT.po b/po/it_IT.po
new file mode 100644
index 00000000..11b66005
--- /dev/null
+++ b/po/it_IT.po
@@ -0,0 +1,319 @@
+# Italian translations for customfetch package.
+# Copyright (C) 2025 THE customfetch'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the customfetch package.
+# Automatically generated, 2025.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: customfetch 0.10.2\n"
+"Report-Msgid-Bugs-To: https://github.com/Toni500github/customfetch\n"
+"POT-Creation-Date: 2025-01-02 20:18+0100\n"
+"PO-Revision-Date: 2025-01-02 20:18+0100\n"
+"Last-Translator: Automatically generated\n"
+"Language-Team: none\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ASCII\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: src/config.cpp:39
+#, c++-format
+msgid "customfetch config folder was not found, Creating folders at {}!"
+msgstr ""
+
+#: src/config.cpp:45
+#, c++-format
+msgid "config file {} not found, generating new one"
+msgstr ""
+
+#: src/config.cpp:60
+#, c++-format
+msgid ""
+"Parsing config file '{}' failed:\n"
+"{}\n"
+"\t(error occurred at line {} column {})"
+msgstr ""
+
+#: src/config.cpp:120
+msgid ""
+"the config array percentage-colors doesn't have 3 colors for being used in "
+"percentage tag and modules\n"
+"backing up to green, yellow and red"
+msgstr ""
+
+#: src/config.cpp:153
+#, c++-format
+msgid "An element of the '{}' array variable is not a string"
+msgstr ""
+
+#: src/config.cpp:167
+#, c++-format
+msgid ""
+"alias color '{}' does NOT have an equal sign '=' for separating color name "
+"and value.\n"
+"for more check with --help"
+msgstr ""
+
+#: src/display.cpp:95 src/gui.cpp:73
+#, c++-format
+msgid "Unable to load image '{}'"
+msgstr ""
+
+#: src/display.cpp:124
+#, c++-format
+msgid ""
+"The image backend '{}' isn't supported, only kitty and viu.\n"
+"Please currently use the GUI mode for rendering the image/gif (use -h for "
+"more details)"
+msgstr ""
+
+#: src/display.cpp:169
+msgid "getpos: error reading response!"
+msgstr ""
+
+#: src/display.cpp:213
+#, c++-format
+msgid "Could not open logo file '{}'"
+msgstr ""
+
+#: src/display.cpp:280
+msgid "images are NOT allowed in the android widget at the moment"
+msgstr ""
+
+#: src/gui.cpp:168
+#, c++-format
+msgid "Background image path '{}' doesn't exist"
+msgstr ""
+
+#: src/main.cpp:373
+#, c++-format
+msgid "config file '{}' doesn't exist"
+msgstr ""
+
+#: src/main.cpp:626
+#, c++-format
+msgid "'{}' doesn't exist. Can't load image/text file"
+msgstr ""
+
+#: src/main.cpp:638
+msgid ""
+"Can't run in GUI mode because it got disabled at compile time\n"
+"Compile customfetch with GUI_MODE=1 or contact your distro to enable it"
+msgstr ""
+
+#: src/parse.cpp:119
+#, c++-format
+msgid ""
+"Parser: failed to parse layout/ascii art: missing 'm' while using ANSI color "
+"escape code in '{}'"
+msgstr ""
+
+#: src/parse.cpp:176
+#, c++-format
+msgid ""
+"ANSI escape code color '\\e[{}' should have an rgb type value\n"
+"e.g \\e[38;2;255;255;255m"
+msgstr ""
+
+#: src/parse.cpp:180
+msgid ""
+"Parser: failed to parse layout/ascii art: missing m while using ANSI color "
+"escape code"
+msgstr ""
+
+#: src/parse.cpp:409
+#, c++-format
+msgid "{} mode in color {} doesn't have close bracket"
+msgstr ""
+
+#: src/parse.cpp:496 src/parse.cpp:776
+#, c++-format
+msgid "256 true color '{}' works only in terminal"
+msgstr ""
+
+#: src/parse.cpp:510 src/parse.cpp:604 src/parse.cpp:794
+#, c++-format
+msgid "PARSER: failed to parse line with color '{}'"
+msgstr ""
+
+#: src/parse.cpp:667
+#, c++-format
+msgid "{} mode in tag color {} doesn't have close bracket"
+msgstr ""
+
+#: src/parse.cpp:859
+#, c++-format
+msgid "percentage tag '{}' doesn't have a comma for separating the 2 numbers"
+msgstr ""
+
+#: src/parse.cpp:904
+#, c++-format
+msgid "PARSER: Missing tag close bracket {} in string '{}'"
+msgstr ""
+
+#: src/parse.cpp:1337
+#, c++-format
+msgid ""
+"seems theme-gtk module name '{}' doesn't have a version number to query.\n"
+"Syntax should be like 'theme_gtkN' which N stands for the version of gtk to "
+"query (single number)"
+msgstr ""
+
+#: src/parse.cpp:1411 src/parse.cpp:1669
+#, c++-format
+msgid "invalid disk module name '{}', must be disk(/path/to/fs) e.g: disk(/)"
+msgstr ""
+
+#: src/parse.cpp:1590 src/parse.cpp:1853
+#, c++-format
+msgid "Invalid module name: {}"
+msgstr ""
+
+#: src/parse.cpp:1812
+#, c++-format
+msgid ""
+"color palette module member '{}' in invalid.\n"
+"Must be used like 'colors_symbol(`symbol for printing the color palette`)'.\n"
+"e.g 'colors_symbol(@)' or 'colors_symbol(string)'"
+msgstr ""
+
+#: src/parse.cpp:1836
+#, c++-format
+msgid ""
+"light color palette module member '{}' in invalid.\n"
+"Must be used like 'colors_light_symbol(`symbol for printing the color "
+"palette`)'.\n"
+"e.g 'colors_light_symbol(@)' or 'colors_light_symbol(string)'"
+msgstr ""
+
+#: src/util.cpp:83
+msgid "Exiting due to CTRL-D or EOF"
+msgstr ""
+
+#: src/util.cpp:97
+msgid "FATAL: $HOME enviroment variable is not set (how?)"
+msgstr ""
+
+#: src/util.cpp:115
+#, c++-format
+msgid "No such enviroment variable: {}"
+msgstr ""
+
+#: src/util.cpp:130
+#, c++-format
+msgid "Failed to open {}"
+msgstr ""
+
+#: src/util.cpp:360
+#, c++-format
+msgid "pipe() failed: {}"
+msgstr ""
+
+#: src/util.cpp:389 src/util.cpp:449
+#, c++-format
+msgid "Failed to execute the command: {}"
+msgstr ""
+
+#: src/util.cpp:402
+#, c++-format
+msgid "An error has occurred with execvp: {}"
+msgstr ""
+
+#: src/util.cpp:408 src/util.cpp:427
+#, c++-format
+msgid "fork() failed: {}"
+msgstr ""
+
+#: src/util.cpp:437
+#, c++-format
+msgid "An error has occurred: {}: {}"
+msgstr ""
+
+#: src/util.cpp:562
+#, c++-format
+msgid "popen() failed: {}"
+msgstr ""
+
+#: src/util.cpp:630
+msgid "Failed to find $HOME, set it to your home directory!"
+msgstr ""
+
+#: src/query/unix/cpu.cpp:204
+#, c++-format
+msgid "Could not open {}"
+msgstr ""
+
+#: src/query/unix/disk.cpp:62
+msgid "setmntent"
+msgstr ""
+
+#: src/query/unix/disk.cpp:63
+msgid "setmntent() failed. Could not get disk info"
+msgstr ""
+
+#: src/query/unix/disk.cpp:85
+msgid "Failed to get disk info"
+msgstr ""
+
+#: src/query/unix/gpu.cpp:104
+msgid "Failed to parse GPU infos on the path /sys/class/drm/"
+msgstr ""
+
+#: src/query/unix/ram.cpp:59
+#, c++-format
+msgid ""
+"Could not open {}\n"
+"Failed to get RAM infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:98
+msgid "Failed to get OS infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:172
+#, c++-format
+msgid ""
+"uname() failed: {}\n"
+"Could not get system infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:175
+#, c++-format
+msgid ""
+"sysinfo() failed: {}\n"
+"Could not get system infos"
+msgstr ""
+
+#: src/query/unix/system.cpp:236
+msgid "/proc/1/comm doesn't exist! (what?)"
+msgstr ""
+
+#: src/query/unix/theme.cpp:179 src/query/unix/theme.cpp:381
+msgid ""
+"customfetch could not detect a gtk configuration file. customfetch will use "
+"the much-slower gsettings."
+msgstr ""
+
+#: src/query/unix/theme.cpp:180 src/query/unix/theme.cpp:382
+msgid ""
+"If there's a file in a standard location that we aren't detecting, please "
+"file an issue on our GitHub."
+msgstr ""
+
+#: src/query/unix/theme.cpp:383
+msgid ""
+"You can disable this warning by disabling slow-query-warnings in your config."
+"toml file."
+msgstr ""
+
+#: src/query/unix/user.cpp:331
+#, c++-format
+msgid ""
+"getpwent failed: {}\n"
+"Could not get user infos"
+msgstr ""
+
+#: src/query/unix/utils/dewm.cpp:201
+msgid "Root node not found"
+msgstr ""
diff --git a/screenshots/android_widget.jpg b/screenshots/android_widget.jpg
new file mode 100644
index 00000000..e6279d05
Binary files /dev/null and b/screenshots/android_widget.jpg differ
diff --git a/screenshots/android_widget2.png b/screenshots/android_widget2.png
new file mode 100644
index 00000000..ae3fe7b3
Binary files /dev/null and b/screenshots/android_widget2.png differ
diff --git a/screenshots/gui-example.png b/screenshots/gui-example.png
new file mode 100644
index 00000000..2bb777ba
Binary files /dev/null and b/screenshots/gui-example.png differ
diff --git a/scripts/make_mo.sh b/scripts/make_mo.sh
new file mode 100755
index 00000000..18adbdb8
--- /dev/null
+++ b/scripts/make_mo.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# original https://github.com/Morganamilo/paru/blob/master/scripts/mkmo
+set -e
+
+if [ -z "$1" ]; then
+ echo "usage: $0 "
+ exit 1
+fi
+
+for po in po/*.po; do
+ lang=$(basename ${po%.po})
+ install -dm755 "$1/$lang/LC_MESSAGES/"
+ msgfmt "$po" -o "$1/$lang/LC_MESSAGES/customfetch.mo"
+done
diff --git a/scripts/make_pot.sh b/scripts/make_pot.sh
new file mode 100755
index 00000000..4ab729d1
--- /dev/null
+++ b/scripts/make_pot.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# original https://github.com/Morganamilo/paru/blob/master/scripts/mkpot
+set -e
+
+xgettext \
+ -d customfetch \
+ --msgid-bugs-address https://github.com/Toni500github/customfetch \
+ --package-name=customfetch\
+ --default-domain=customfetch\
+ --package-version="$(awk -F '= ' '/^VERSION/ {print $2}' Makefile | sed 's/\"//g')" \
+ -k_ \
+ -o po/customfetch.pot \
+ src/*.cpp src/query/android/*.cpp src/query/unix/*.cpp src/query/unix/utils/*.cpp --c++
diff --git a/scripts/newpo.sh b/scripts/newpo.sh
new file mode 100755
index 00000000..600feaf8
--- /dev/null
+++ b/scripts/newpo.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+set -e
+
+if [ -z "$1" ]; then
+ printf "usage: $0 (lang)\nexample: $0 en_US"
+ exit 1
+fi
+
+if [ ! -f "po/customfetch.pot" ]; then
+ printf "failed to get po/customfetch.pot\nplease run scripts/make_pot.sh first if haven't yet"
+ exit 1
+fi
+
+if [ -f "po/$1.po" ]; then
+ printf "po/$1.po already exists\nmaybe modify it if you want to contribute"
+ exit 1
+fi
+
+msginit --no-translator --locale "$1" -o "po/$1.po" --input "po/customfetch.pot"
diff --git a/scripts/updpo.sh b/scripts/updpo.sh
new file mode 100755
index 00000000..c653e108
--- /dev/null
+++ b/scripts/updpo.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -e
+
+for po in po/*.po; do
+ msgmerge "$po" "po/customfetch.pot" -o "$po"
+done
diff --git a/src/config.cpp b/src/config.cpp
index e4f3a457..69061aba 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -32,21 +32,24 @@
#include "util.hpp"
-Config::Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors)
+Config::Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors, bool do_not_load)
{
if (!std::filesystem::exists(configDir))
{
- warn("customfetch config folder was not found, Creating folders at {}!", configDir);
+#if !ANDROID_APP
+ warn(_("customfetch config folder was not found, Creating folders at {}!"), configDir);
+#endif
std::filesystem::create_directories(configDir);
}
if (!std::filesystem::exists(configFile))
{
- warn("config file {} not found, generating new one", configFile);
+ warn(_("config file {} not found, generating new one"), configFile);
this->generateConfig(configFile);
}
- this->loadConfigFile(configFile, colors);
+ if (!do_not_load)
+ this->loadConfigFile(configFile, colors);
}
void Config::loadConfigFile(const std::string_view filename, colors_t& colors)
@@ -57,9 +60,11 @@ void Config::loadConfigFile(const std::string_view filename, colors_t& colors)
}
catch (const toml::parse_error& err)
{
- error("Parsing config file {} failed:", filename);
- std::cerr << err << std::endl;
- exit(-1);
+ die(_("Parsing config file '{}' failed:\n"
+ "{}\n"
+ "\t(error occurred at line {} column {})"),
+ filename, err.description(),
+ err.source().begin.line, err.source().begin.column);
}
// clang-format off
@@ -70,7 +75,7 @@ void Config::loadConfigFile(const std::string_view filename, colors_t& colors)
this->slow_query_warnings = getValue("config.slow-query-warnings", false);
this->sep_reset_after = getValue("config.sep-reset-after", false);
this->use_SI_unit = getValue("config.use-SI-byte-unit", false);
- this->wrap_lines = getValue("config.wrap-lines", true);
+ this->wrap_lines = getValue("config.wrap-lines", false);
this->offset = getValue("config.offset", 5);
this->logo_padding_left = getValue("config.logo-padding-left", 0);
this->layout_padding_top = getValue("config.layout-padding-top", 0);
@@ -115,8 +120,8 @@ void Config::loadConfigFile(const std::string_view filename, colors_t& colors)
if (this->percentage_colors.size() < 3)
{
- warn("the config array percentage-colors doesn't have 3 colors for being used in percentage tag and modules\n"
- "backing up to green, yellow and red");
+ warn(_("the config array percentage-colors doesn't have 3 colors for being used in percentage tag and modules\n"
+ "backing up to green, yellow and red"));
this->percentage_colors = {"green", "yellow", "red"};
}
@@ -128,7 +133,6 @@ void Config::loadConfigFile(const std::string_view filename, colors_t& colors)
this->m_disable_colors = true;
}
-// Config::getValue() but don't want to specify the template
std::string Config::getThemeValue(const std::string_view value, const std::string_view fallback) const
{
return this->tbl.at_path(value).value().value_or(fallback.data());
@@ -147,12 +151,9 @@ std::vector Config::getValueArrayStr(const std::string_view
[&ret, value](auto&& el)
{
if (const toml::value* str_elem = el.as_string())
- {
- const toml::value& v = *str_elem;
- ret.push_back(v->data());
- }
+ ret.push_back((*str_elem)->data());
else
- warn("An element of the {} array variable is not a string", value);
+ warn(_("An element of the '{}' array variable is not a string"), value);
}
);
}
@@ -166,8 +167,8 @@ void Config::addAliasColors(const std::string& str)
{
const size_t pos = str.find('=');
if (pos == std::string::npos)
- die("alias color '{}' does NOT have an equal sign '=' for separiting color name and value.\n"
- "for more check with --help", str);
+ die(_("alias color '{}' does NOT have an equal sign '=' for separating color name and value.\n"
+ "for more check with --help"), str);
const std::string& name = str.substr(0, pos);
const std::string& value = str.substr(pos + 1);
@@ -178,11 +179,13 @@ void Config::addAliasColors(const std::string& str)
void Config::generateConfig(const std::string_view filename)
{
+#if !ANDROID_APP
if (std::filesystem::exists(filename))
{
if (!askUserYorN(false, "WARNING: config file {} already exists. Do you want to overwrite it?", filename))
std::exit(1);
}
+#endif
std::ofstream f(filename.data(), std::ios::trunc);
f << AUTOCONFIG;
diff --git a/src/display.cpp b/src/display.cpp
index be7a510f..f3dd335d 100644
--- a/src/display.cpp
+++ b/src/display.cpp
@@ -71,11 +71,16 @@ std::string Display::detect_distro(const Config& config)
format = fmt::format("{}/ascii/{}.txt", config.data_dir, str_tolower(system.os_name()));
if (std::filesystem::exists(format))
return format;
-
- return config.data_dir + "/ascii/linux.txt";
}
+#if ANDROID_APP
+ return config.data_dir + "/ascii/android.txt";
+#else
+ return config.data_dir + "/ascii/linux.txt";
+#endif
+
}
+#if !ANDROID_APP
static std::vector render_with_image(systemInfo_t& systemInfo, std::vector& layout,
const Config& config, const colors_t& colors,
const std::string_view path, const std::uint16_t font_width,
@@ -87,17 +92,18 @@ static std::vector render_with_image(systemInfo_t& systemInfo, std:
unsigned char* img = stbi_load(path.data(), &image_width, &image_height, &channels, 0);
if (!img)
- die("Unable to load image '{}'", path);
+ die(_("Unable to load image '{}'"), path);
stbi_image_free(img);
std::string _;
- parse_args_t parse_args{ systemInfo, _, config, colors, true, true };
+ parse_args_t parse_args{ systemInfo, _, config, colors, true, true, false, "" };
for (std::string& line : layout)
{
line = parse(line, parse_args);
if (!config.m_disable_colors)
line.insert(0, NOCOLOR);
+ parse_args.no_more_reset = false;
}
// erase each element for each instance of MAGIC_LINE
@@ -115,8 +121,8 @@ static std::vector render_with_image(systemInfo_t& systemInfo, std:
else if (config.m_image_backend == "viu")
taur_exec({ "viu", "-t", "-w", fmt::to_string(width), "-h", fmt::to_string(height), path });
else
- die("The image backend '{}' isn't supported, only kitty and viu.\n"
- "Please currently use the GUI mode for rendering the image/gif (use -h for more details)",
+ die(_("The image backend '{}' isn't supported, only kitty and viu.\n"
+ "Please currently use the GUI mode for rendering the image/gif (use -h for more details)"),
config.m_image_backend);
if (config.logo_position == "top")
@@ -127,9 +133,9 @@ static std::vector render_with_image(systemInfo_t& systemInfo, std:
return layout;
}
- for (size_t i = 0; i < layout.size(); ++i)
+ for (auto& str : layout)
for (size_t _ = 0; _ < width + config.offset; ++_)
- layout.at(i).insert(0, " ");
+ str.insert(0, " ");
return layout;
}
@@ -160,7 +166,7 @@ static bool get_pos(int& y, int& x)
if (!ret)
{
tcsetattr(0, TCSANOW, &restore);
- die("getpos: error reading response!");
+ die(_("getpos: error reading response!"));
}
buf.at(i) = ch;
}
@@ -180,6 +186,7 @@ static bool get_pos(int& y, int& x)
tcsetattr(0, TCSANOW, &restore);
return true;
}
+#endif
std::vector Display::render(const Config& config, const colors_t& colors, const bool already_analyzed_file,
const std::string_view path)
@@ -189,6 +196,12 @@ std::vector Display::render(const Config& config, const colors_t& c
debug("Display::render path = {}", path);
+#if ANDROID_APP
+ const std::string_view space = " ";
+#else
+ const std::string_view space = " ";
+#endif
+
bool isImage = false;
std::ifstream file;
std::ifstream fileToAnalyze; // both have same path
@@ -197,7 +210,7 @@ std::vector Display::render(const Config& config, const colors_t& c
file.open(path.data(), std::ios::binary);
fileToAnalyze.open(path.data(), std::ios::binary);
if (!file.is_open() || !fileToAnalyze.is_open())
- die("Could not open ascii art file \"{}\"", path);
+ die(_("Could not open logo file '{}'"), path);
// first check if the file is an image
// without even using the same library that "file" uses
@@ -230,15 +243,19 @@ std::vector Display::render(const Config& config, const colors_t& c
std::ifstream distro_file(distro_path);
std::string line, _;
- parse_args_t parse_args{ systemInfo, _, config, colors, false, true };
+ parse_args_t parse_args{ systemInfo, _, config, colors, false, true, false, "" };
while (std::getline(distro_file, line))
+ {
parse(line, _, parse_args);
+ parse_args.no_more_reset = false;
+ }
}
std::vector pureAsciiArtLens;
int maxLineLength = -1;
+#if !ANDROID_APP
if (isImage)
{
// clear screen
@@ -257,14 +274,20 @@ std::vector Display::render(const Config& config, const colors_t& c
return render_with_image(systemInfo, layout, config, colors, path, font_width, font_height);
}
+#else
+ if (isImage)
+ {
+ die(_("images are NOT allowed in the android widget at the moment"));
+ }
+#endif
- for (int i = 0; i < config.logo_padding_top; i++)
+ for (uint i = 0; i < config.logo_padding_top; i++)
{
pureAsciiArtLens.push_back(0);
- asciiArt.push_back("");
+ asciiArt.emplace_back("");
}
- for (int i = 0; i < config.layout_padding_top; i++)
+ for (uint i = 0; i < config.layout_padding_top; i++)
{
layout.insert(layout.begin(), "");
}
@@ -273,16 +296,17 @@ std::vector Display::render(const Config& config, const colors_t& c
while (std::getline(file, line))
{
std::string pureOutput;
- parse_args_t parse_args{ systemInfo, pureOutput, config, colors, false, true };
+ parse_args_t parse_args{ systemInfo, pureOutput, config, colors, false, true, false, "" };
std::string asciiArt_s = parse(line, parse_args);
+ parse_args.no_more_reset = false;
if (!config.m_disable_colors)
asciiArt_s += config.gui ? "" : NOCOLOR;
if (config.gui)
{
// check parse.cpp
- const size_t pos = asciiArt_s.rfind("$ ");
+ const size_t pos = asciiArt_s.rfind("$ ");
if (pos != std::string::npos)
asciiArt_s.replace(pos, 2, "$");
}
@@ -301,22 +325,27 @@ std::vector Display::render(const Config& config, const colors_t& c
return asciiArt;
std::string _;
- parse_args_t parse_args{ systemInfo, _, config, colors, true, true };
+ parse_args_t parse_args{ systemInfo, _, config, colors, true, true, false, "" };
for (std::string& line : layout)
{
line = parse(line, parse_args);
+ parse_args.no_more_reset = false;
if (!config.gui && !config.m_disable_colors)
line.insert(0, NOCOLOR);
}
+
+ auto_colors.clear();
// erase each element for each instance of MAGIC_LINE
layout.erase(std::remove_if(layout.begin(), layout.end(),
[](const std::string_view str) { return str.find(MAGIC_LINE) != std::string::npos; }),
layout.end());
- if (config.logo_position == "top")
+ if (config.logo_position == "top" || config.logo_position == "bottom")
{
- Display::display(asciiArt);
+ if (!asciiArt.empty())
+ layout.insert(config.logo_position == "top" ? layout.begin() : layout.end(),
+ asciiArt.begin(), asciiArt.end());
return layout;
}
@@ -327,7 +356,7 @@ std::vector Display::render(const Config& config, const colors_t& c
// The user-specified offset to be put before the logo
for (size_t j = 0; j < config.logo_padding_left; j++)
- layout.at(i).insert(0, " ");
+ layout.at(i).insert(0, space);
if (i < asciiArt.size())
{
@@ -341,10 +370,11 @@ std::vector Display::render(const Config& config, const colors_t& c
debug("spaces: {}", spaces);
for (size_t j = 0; j < spaces; j++)
- layout.at(i).insert(origin, " ");
+ layout.at(i).insert(origin, space);
if (!config.m_disable_colors)
layout.at(i) += config.gui ? "" : NOCOLOR;
+
}
for (; i < asciiArt.size(); i++)
@@ -353,7 +383,7 @@ std::vector Display::render(const Config& config, const colors_t& c
line.reserve(config.logo_padding_left + asciiArt.at(i).length());
for (size_t j = 0; j < config.logo_padding_left; j++)
- line += ' ';
+ line += space;
line += asciiArt.at(i);
@@ -366,5 +396,5 @@ std::vector Display::render(const Config& config, const colors_t& c
void Display::display(const std::vector& renderResult)
{
for (const std::string& str : renderResult)
- fmt::println("{}", str);
+ fmt::print("{}\n", str);
}
diff --git a/src/fmt/CMakeLists.txt b/src/fmt/CMakeLists.txt
new file mode 100644
index 00000000..1606e886
--- /dev/null
+++ b/src/fmt/CMakeLists.txt
@@ -0,0 +1,526 @@
+cmake_minimum_required(VERSION 3.8...3.28)
+
+# Fallback for using newer policies on CMake <3.12.
+if (${CMAKE_VERSION} VERSION_LESS 3.12)
+ cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
+endif ()
+
+# Determine if fmt is built as a subproject (using add_subdirectory)
+# or if it is the master project.
+if (NOT DEFINED FMT_MASTER_PROJECT)
+ set(FMT_MASTER_PROJECT OFF)
+ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
+ set(FMT_MASTER_PROJECT ON)
+ message(STATUS "CMake version: ${CMAKE_VERSION}")
+ endif ()
+endif ()
+
+# Joins arguments and places the results in ${result_var}.
+function(join result_var)
+ set(result "")
+ foreach (arg ${ARGN})
+ set(result "${result}${arg}")
+ endforeach ()
+ set(${result_var} "${result}" PARENT_SCOPE)
+endfunction()
+
+# DEPRECATED! Should be merged into add_module_library.
+function(enable_module target)
+ if (MSVC)
+ set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
+ target_compile_options(${target}
+ PRIVATE /interface /ifcOutput ${BMI}
+ INTERFACE /reference fmt=${BMI})
+ set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
+ set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
+ endif ()
+endfunction()
+
+set(FMT_USE_CMAKE_MODULES FALSE)
+if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND
+ CMAKE_GENERATOR STREQUAL "Ninja")
+ set(FMT_USE_CMAKE_MODULES TRUE)
+endif ()
+
+# Adds a library compiled with C++20 module support.
+# `enabled` is a CMake variables that specifies if modules are enabled.
+# If modules are disabled `add_module_library` falls back to creating a
+# non-modular library.
+#
+# Usage:
+# add_module_library( [sources...] FALLBACK [sources...] [IF enabled])
+function(add_module_library name)
+ cmake_parse_arguments(AML "" "IF" "FALLBACK" ${ARGN})
+ set(sources ${AML_UNPARSED_ARGUMENTS})
+
+ add_library(${name})
+ set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX)
+
+ if (NOT ${${AML_IF}})
+ # Create a non-modular library.
+ target_sources(${name} PRIVATE ${AML_FALLBACK})
+ set_target_properties(${name} PROPERTIES CXX_SCAN_FOR_MODULES OFF)
+ return()
+ endif ()
+
+ # Modules require C++20.
+ target_compile_features(${name} PUBLIC cxx_std_20)
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ target_compile_options(${name} PUBLIC -fmodules-ts)
+ endif ()
+
+ target_compile_definitions(${name} PRIVATE FMT_MODULE)
+
+ if (FMT_USE_CMAKE_MODULES)
+ target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES
+ FILES ${sources})
+ else()
+ # `std` is affected by CMake options and may be higher than C++20.
+ get_target_property(std ${name} CXX_STANDARD)
+
+ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(pcms)
+ foreach (src ${sources})
+ get_filename_component(pcm ${src} NAME_WE)
+ set(pcm ${pcm}.pcm)
+
+ # Propagate -fmodule-file=*.pcm to targets that link with this library.
+ target_compile_options(
+ ${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
+
+ # Use an absolute path to prevent target_link_libraries prepending -l
+ # to it.
+ set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm})
+ add_custom_command(
+ OUTPUT ${pcm}
+ COMMAND ${CMAKE_CXX_COMPILER}
+ -std=c++${std} -x c++-module --precompile -c
+ -o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
+ "-I$,;-I>"
+ # Required by the -I generator expression above.
+ COMMAND_EXPAND_LISTS
+ DEPENDS ${src})
+ endforeach ()
+
+ # Add .pcm files as sources to make sure they are built before the library.
+ set(sources)
+ foreach (pcm ${pcms})
+ get_filename_component(pcm_we ${pcm} NAME_WE)
+ set(obj ${pcm_we}.o)
+ # Use an absolute path to prevent target_link_libraries prepending -l.
+ set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj})
+ add_custom_command(
+ OUTPUT ${obj}
+ COMMAND ${CMAKE_CXX_COMPILER} $
+ -c -o ${obj} ${pcm}
+ DEPENDS ${pcm})
+ endforeach ()
+ endif ()
+ target_sources(${name} PRIVATE ${sources})
+ endif()
+endfunction()
+
+include(CMakeParseArguments)
+
+# Sets a cache variable with a docstring joined from multiple arguments:
+# set(... CACHE ...)
+# This allows splitting a long docstring for readability.
+function(set_verbose)
+ # cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
+ # list instead.
+ list(GET ARGN 0 var)
+ list(REMOVE_AT ARGN 0)
+ list(GET ARGN 0 val)
+ list(REMOVE_AT ARGN 0)
+ list(REMOVE_AT ARGN 0)
+ list(GET ARGN 0 type)
+ list(REMOVE_AT ARGN 0)
+ join(doc ${ARGN})
+ set(${var} ${val} CACHE ${type} ${doc})
+endfunction()
+
+# Set the default CMAKE_BUILD_TYPE to Release.
+# This should be done before the project command since the latter can set
+# CMAKE_BUILD_TYPE itself (it does so for nmake).
+if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
+ set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
+ "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
+ "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
+endif ()
+
+project(FMT CXX)
+include(GNUInstallDirs)
+set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
+ "Installation directory for include files, a relative path that "
+ "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
+
+option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
+option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
+ OFF)
+
+# Options that control generation of various targets.
+option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
+option(FMT_INSTALL "Generate the install target." ON)
+option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
+option(FMT_FUZZ "Generate the fuzz target." OFF)
+option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
+option(FMT_OS "Include OS-specific APIs." ON)
+option(FMT_MODULE "Build a module instead of a traditional library." OFF)
+option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
+option(FMT_UNICODE "Enable Unicode support." ON)
+
+if (FMT_TEST AND FMT_MODULE)
+ # The tests require {fmt} to be compiled as traditional library
+ message(STATUS "Testing is incompatible with build mode 'module'.")
+endif ()
+set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
+if (FMT_SYSTEM_HEADERS)
+ set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
+endif ()
+if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
+ set(FMT_TEST OFF)
+ message(STATUS "MSDOS is incompatible with gtest")
+endif ()
+
+# Get version from base.h
+file(READ include/fmt/base.h base_h)
+if (NOT base_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
+ message(FATAL_ERROR "Cannot get FMT_VERSION from base.h.")
+endif ()
+# Use math to skip leading zeros if any.
+math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
+math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
+math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
+join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.
+ ${CPACK_PACKAGE_VERSION_PATCH})
+message(STATUS "{fmt} version: ${FMT_VERSION}")
+
+message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
+
+if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
+endif ()
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
+ "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
+
+include(CheckCXXCompilerFlag)
+include(JoinPaths)
+
+if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET)
+ set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING
+ "Preset for the export of private symbols")
+ set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS
+ hidden default)
+endif ()
+
+if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN)
+ set_verbose(CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL
+ "Whether to add a compile flag to hide symbols of inline functions")
+endif ()
+
+if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+ set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
+ -Wold-style-cast -Wundef
+ -Wredundant-decls -Wwrite-strings -Wpointer-arith
+ -Wcast-qual -Wformat=2 -Wmissing-include-dirs
+ -Wcast-align
+ -Wctor-dtor-privacy -Wdisabled-optimization
+ -Winvalid-pch -Woverloaded-virtual
+ -Wconversion -Wundef
+ -Wno-ctor-dtor-privacy -Wno-format-nonliteral)
+ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
+ set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
+ -Wno-dangling-else -Wno-unused-local-typedefs)
+ endif ()
+ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
+ set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
+ -Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
+ -Wvector-operation-performance -Wsized-deallocation -Wshadow)
+ endif ()
+ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
+ set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
+ -Wduplicated-cond)
+ # Workaround for GCC regression
+ # [12/13/14/15 regression] New (since gcc 12) false positive null-dereference in vector.resize
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108860
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
+ set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnull-dereference)
+ endif ()
+ endif ()
+ set(WERROR_FLAG -Werror)
+endif ()
+
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
+ -Wdeprecated -Wweak-vtables -Wshadow
+ -Wno-gnu-zero-variadic-macro-arguments)
+ check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
+ if (HAS_NULLPTR_WARNING)
+ set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
+ -Wzero-as-null-pointer-constant)
+ endif ()
+ set(WERROR_FLAG -Werror)
+endif ()
+
+if (MSVC)
+ set(PEDANTIC_COMPILE_FLAGS /W3)
+ set(WERROR_FLAG /WX)
+endif ()
+
+if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
+ # If Microsoft SDK is installed create script run-msbuild.bat that
+ # calls SetEnv.cmd to set up build environment and runs msbuild.
+ # It is useful when building Visual Studio projects with the SDK
+ # toolchain rather than Visual Studio.
+ include(FindSetEnv)
+ if (WINSDK_SETENV)
+ set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
+ endif ()
+ # Set FrameworkPathOverride to get rid of MSB3644 warnings.
+ join(netfxpath
+ "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
+ ".NETFramework\\v4.0")
+ file(WRITE run-msbuild.bat "
+ ${MSBUILD_SETUP}
+ ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
+endif ()
+
+function(add_headers VAR)
+ set(headers ${${VAR}})
+ foreach (header ${ARGN})
+ set(headers ${headers} include/fmt/${header})
+ endforeach()
+ set(${VAR} ${headers} PARENT_SCOPE)
+endfunction()
+
+# Define the fmt library, its includes and the needed defines.
+add_headers(FMT_HEADERS args.h base.h chrono.h color.h compile.h core.h format.h
+ format-inl.h os.h ostream.h printf.h ranges.h std.h
+ xchar.h)
+set(FMT_SOURCES src/format.cc)
+
+add_module_library(fmt src/fmt.cc FALLBACK
+ ${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md
+ IF FMT_MODULE)
+add_library(fmt::fmt ALIAS fmt)
+if (FMT_MODULE)
+ enable_module(fmt)
+elseif (FMT_OS)
+ target_sources(fmt PRIVATE src/os.cc)
+else()
+ target_compile_definitions(fmt PRIVATE FMT_OS=0)
+endif ()
+
+if (FMT_WERROR)
+ target_compile_options(fmt PRIVATE ${WERROR_FLAG})
+endif ()
+if (FMT_PEDANTIC)
+ target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
+endif ()
+
+if (cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+ target_compile_features(fmt PUBLIC cxx_std_11)
+else ()
+ message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler")
+endif ()
+
+target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE PUBLIC
+ $
+ $)
+
+set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
+
+set_target_properties(fmt PROPERTIES
+ VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
+ PUBLIC_HEADER "${FMT_HEADERS}"
+ DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}"
+
+ # Workaround for Visual Studio 2017:
+ # Ensure the .pdb is created with the same name and in the same directory
+ # as the .lib. Newer VS versions already do this by default, but there is no
+ # harm in setting it for those too. Ignored by other generators.
+ COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
+ COMPILE_PDB_NAME "fmt"
+ COMPILE_PDB_NAME_DEBUG "fmt${FMT_DEBUG_POSTFIX}")
+
+# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
+# property because it's not set by default.
+set(FMT_LIB_NAME fmt)
+if (CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
+endif ()
+
+if (BUILD_SHARED_LIBS)
+ target_compile_definitions(fmt PRIVATE FMT_LIB_EXPORT INTERFACE FMT_SHARED)
+endif ()
+if (FMT_SAFE_DURATION_CAST)
+ target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
+endif ()
+
+add_library(fmt-header-only INTERFACE)
+add_library(fmt::fmt-header-only ALIAS fmt-header-only)
+
+if (NOT MSVC)
+ # Unicode is always supported on compilers other than MSVC.
+elseif (FMT_UNICODE)
+ # Unicode support requires compiling with /utf-8.
+ target_compile_options(fmt PUBLIC $<$,$>:/utf-8>)
+ target_compile_options(fmt-header-only INTERFACE $<$,$>:/utf-8>)
+else ()
+ target_compile_definitions(fmt PUBLIC FMT_UNICODE=0)
+endif ()
+
+target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
+target_compile_features(fmt-header-only INTERFACE cxx_std_11)
+
+target_include_directories(fmt-header-only
+ ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE INTERFACE
+ $
+ $)
+
+# Install targets.
+if (FMT_INSTALL)
+ include(CMakePackageConfigHelpers)
+ set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
+ "Installation directory for cmake files, a relative path that "
+ "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute "
+ "path.")
+ set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
+ set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
+ set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
+ set(targets_export_name fmt-targets)
+
+ set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
+ "Installation directory for libraries, a relative path that "
+ "will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.")
+
+ set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE STRING
+ "Installation directory for pkgconfig (.pc) files, a relative "
+ "path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
+ "absolute path.")
+
+ # Generate the version, config and target files into the build directory.
+ write_basic_package_version_file(
+ ${version_config}
+ VERSION ${FMT_VERSION}
+ COMPATIBILITY AnyNewerVersion)
+
+ join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}")
+ join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}")
+
+ configure_file(
+ "${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
+ "${pkgconfig}"
+ @ONLY)
+ configure_package_config_file(
+ ${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
+ ${project_config}
+ INSTALL_DESTINATION ${FMT_CMAKE_DIR})
+
+ set(INSTALL_TARGETS fmt fmt-header-only)
+
+ set(INSTALL_FILE_SET)
+ if (FMT_USE_CMAKE_MODULES)
+ set(INSTALL_FILE_SET FILE_SET fmt DESTINATION "${FMT_INC_DIR}/fmt")
+ endif()
+
+ # Install the library and headers.
+ install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
+ LIBRARY DESTINATION ${FMT_LIB_DIR}
+ ARCHIVE DESTINATION ${FMT_LIB_DIR}
+ PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ ${INSTALL_FILE_SET})
+
+ # Use a namespace because CMake provides better diagnostics for namespaced
+ # imported targets.
+ export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
+ FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
+
+ # Install version, config and target files.
+ install(
+ FILES ${project_config} ${version_config}
+ DESTINATION ${FMT_CMAKE_DIR})
+ install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
+ NAMESPACE fmt::)
+
+ install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
+endif ()
+
+function(add_doc_target)
+ find_program(DOXYGEN doxygen
+ PATHS "$ENV{ProgramFiles}/doxygen/bin"
+ "$ENV{ProgramFiles\(x86\)}/doxygen/bin")
+ if (NOT DOXYGEN)
+ message(STATUS "Target 'doc' disabled because doxygen not found")
+ return ()
+ endif ()
+
+ find_program(MKDOCS mkdocs)
+ if (NOT MKDOCS)
+ message(STATUS "Target 'doc' disabled because mkdocs not found")
+ return ()
+ endif ()
+
+ set(sources )
+ foreach (source api.md index.md syntax.md get-started.md fmt.css fmt.js)
+ set(sources ${sources} doc/${source})
+ endforeach()
+
+ add_custom_target(
+ doc
+ COMMAND
+ ${CMAKE_COMMAND}
+ -E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/support/python
+ ${MKDOCS} build -f ${CMAKE_CURRENT_SOURCE_DIR}/support/mkdocs.yml
+ # MkDocs requires the site dir to be outside of the doc dir.
+ --site-dir ${CMAKE_CURRENT_BINARY_DIR}/doc-html
+ --no-directory-urls
+ SOURCES ${sources})
+
+ include(GNUInstallDirs)
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
+ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL)
+endfunction()
+
+if (FMT_DOC)
+ add_doc_target()
+endif ()
+
+if (FMT_TEST)
+ enable_testing()
+ add_subdirectory(test)
+endif ()
+
+# Control fuzzing independent of the unit tests.
+if (FMT_FUZZ)
+ add_subdirectory(test/fuzzing)
+
+ # The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing
+ # mode and make fuzzing practically possible. It is similar to
+ # FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to
+ # avoid interfering with fuzzing of projects that use {fmt}.
+ # See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode.
+ target_compile_definitions(fmt PUBLIC FMT_FUZZ)
+endif ()
+
+set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
+if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
+ # Get the list of ignored files from .gitignore.
+ file (STRINGS ${gitignore} lines)
+ list(REMOVE_ITEM lines /doc/html)
+ foreach (line ${lines})
+ string(REPLACE "." "[.]" line "${line}")
+ string(REPLACE "*" ".*" line "${line}")
+ set(ignored_files ${ignored_files} "${line}$" "${line}/")
+ endforeach ()
+ set(ignored_files ${ignored_files} /.git /build/doxyxml .vagrant)
+
+ set(CPACK_SOURCE_GENERATOR ZIP)
+ set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})
+ set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION})
+ set(CPACK_PACKAGE_NAME fmt)
+ set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
+ include(CPack)
+endif ()
diff --git a/src/gui.cpp b/src/gui.cpp
index ee4ebc50..95db4a14 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -23,7 +23,7 @@
*
*/
-#ifdef GUI_MODE
+#if GUI_MODE && !ANDROID_APP
#define STB_IMAGE_IMPLEMENTATION
#include "gui.hpp"
@@ -70,7 +70,7 @@ static std::vector render_with_image(const Config& config, const co
unsigned char* img = stbi_load(config.source_path.c_str(), &image_width, &image_height, &channels, 0);
if (!img)
- die("Unable to load image '{}'", config.source_path);
+ die(_("Unable to load image '{}'"), config.source_path);
stbi_image_free(img);
@@ -90,11 +90,17 @@ static std::vector render_with_image(const Config& config, const co
parse_args_t parse_args{ systemInfo, _, config, colors, false, true };
while (std::getline(file, line))
+ {
parse(line, parse_args);
+ parse_args.no_more_reset = false;
+ }
parse_args.parsingLayout = true;
for (std::string& layout : layout)
+ {
layout = parse(layout, parse_args);
+ parse_args.no_more_reset = false;
+ }
// erase each element for each instance of MAGIC_LINE
layout.erase(std::remove_if(layout.begin(), layout.end(),
@@ -157,10 +163,12 @@ Window::Window(const Config& config, const colors_t& colors, const std::string_v
m_label.set_markup(fmt::format("{}", fmt::join(Display::render(config, colors, true, path), "\n")));
}
+ auto_colors.clear();
+
if (config.gui_bg_image != "disable")
{
if (!std::filesystem::exists(config.gui_bg_image))
- die("Background image path '{}' doesn't exist", config.gui_bg_image);
+ die(_("Background image path '{}' doesn't exist"), config.gui_bg_image);
m_bg_animation = Gdk::PixbufAnimation::create_from_file(config.gui_bg_image);
if (m_bg_animation->is_static_image())
@@ -191,4 +199,4 @@ Window::Window(const Config& config, const colors_t& colors, const std::string_v
Window::~Window() {}
-#endif // GUI_MODE
+#endif // GUI_MODE && !ANDROID_APP
diff --git a/src/main.cpp b/src/main.cpp
index b6f82286..25893e68 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -27,9 +27,11 @@
#include
#include
+#include
#include "config.hpp"
#include "display.hpp"
+#include "fmt/ranges.h"
#include "gui.hpp"
#include "switch_fnv1a.hpp"
#include "util.hpp"
@@ -46,41 +48,57 @@
using namespace std::string_view_literals;
-static void version()
+#if ANDROID_APP
+#define STRING_IF_ANDROID_APP_ELSE(x) std::string
+#define RETURN_OR_PRINT(x) return x
+#define RETURN_IF_ANDROID_APP return
+#define _true "true"
+#define _false "failed"
+#else
+#define STRING_IF_ANDROID_APP_ELSE(x) x
+#define RETURN_OR_PRINT(x) fmt::print("{}\n", x)
+#define RETURN_IF_ANDROID_APP
+#define _true true
+#define _false false
+#endif
+
+static STRING_IF_ANDROID_APP_ELSE(void) version()
{
- fmt::println("customfetch {} branch {}", VERSION, BRANCH);
+ std::string version{"customfetch " VERSION " branch " BRANCH "\n"};
-#ifdef GUI_MODE
- fmt::print("GUI mode enabled\n\n");
+#if GUI_MODE
+ version += "GUI mode enabled\n\n";
#else
- fmt::print("GUI mode IS NOT enabled\n\n");
+ version += "GUI mode IS NOT enabled\n\n";
#endif
#if !(USE_DCONF)
- fmt::println("NO flags were set");
+ version += "NO flags were set\n";
#else
- fmt::println("set flags:");
+ version += "set flags:\n";
#if USE_DCONF
- fmt::println("USE_DCONF");
+ version += "USE_DCONF\n";
#endif
#endif
+ RETURN_OR_PRINT(version);
+
// if only everyone would not return error when querying the program version :(
std::exit(EXIT_SUCCESS);
}
-static void help(bool invalid_opt = false)
+static STRING_IF_ANDROID_APP_ELSE(void) help(bool invalid_opt = false)
{
- fmt::println("Usage: customfetch [OPTIONS]...");
- fmt::println(R"(
-A command-line system information tool (or neofetch like program), which its focus point is customizability and perfomance
-
- -n, --no-display Do not display the logo
- -N, --no-color Do not output and parse colors. Useful for stdout or pipe operations
- --enable-colors Inverse of --no-color
- -s, --source-path Path to the ascii art or image file to display
- -C, --config Path to the config file to use
- -a, --ascii-logo-type []
+ constexpr std::string_view help(R"(Usage: customfetch [OPTIONS]...
+A command-line, GUI app, android widget system information tool (or neofetch like program), which its focus point is customizability and performance.
+
+NOTE: Arguments that takes [] values, the values can be either: true, 1, enable or leave it empty. Any other value will be treated as false.
+
+ -n, --no-logo [] Do not display the logo
+ -N, --no-color [] Do not output and parse colors. Useful for stdout or pipe operations
+ -s, --source-path Path to the ascii art or image file to display
+ -C, --config Path to the config file to use
+ -a, --ascii-logo-type []
The type of ASCII art to apply ("small" or "old").
Basically will add "_" to the logo filename.
It will return the regular linux ascii art if it doesn't exist.
@@ -91,51 +109,53 @@ A command-line system information tool (or neofetch like program), which its foc
-f, --font The font to be used in GUI mode (syntax must be "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]" without the double quotes and [])
An example: [Liberation Mono] [Normal] [12], which can be "Liberation Mono Normal 12"
- -i, --image-backend (EXPERIMENTAL) Image backend tool for displaying images in terminal.
+ -i, --image-backend (EXPERIMENTAL) Image backend tool for displaying images in terminal.
Right now only 'kitty' and 'viu' are supported
- It's recommended to use GUI mode for the moment if something doesn't work
+ It's recommended to use GUI mode for the moment if something doesn't work
- -m, --layout-line Will replace the config layout, with a layout you specify in the arguments
- Example: `customfetch -m "${{auto}}OS: $" -m "${{auto}}CPU: $"`
- Will only print the logo (if not disabled), along side the parsed OS and CPU
+ -m, --layout-line Will replace the config layout, with a layout you specify in the arguments
+ Example: `customfetch -m "${auto}OS: $" -m "${auto}CPU: $"`
+ Will only print the logo (if not disabled), along side the parsed OS and CPU
- -g, --gui Use GUI mode instead of priting in the terminal (use --version to check if it was enabled)
- -p, --logo-position Position of the logo ("top" or "left")
+ -g, --gui Use GUI mode instead of printing in the terminal (use --version to check if it was enabled)
+ -p, --logo-position Position of the logo ("top" or "left" or "bottom")
-o, --offset Offset between the ascii art and the layout
- -l. --list-modules Print the list of the modules and its members
- -h, --help Print this help menu
- -L, --logo-only Print only the logo
- -V, --version Print the version along with the git branch it was built
+ -l. --list-modules Print the list of the modules and its members
+ -L, --logo-only [] Print only the logo
+ -h, --help Print this help menu
+ -V, --version Print the version along with the git branch it was built
--bg-image Path to image to be used in the background in GUI (put "disable" for disabling in the config)
- --wrap-lines [<0,1>] Disable (0) or Enable (1) wrapping lines when printing in terminal
- --logo-padding-top Padding of the logo from the top
- --logo-padding-left Padding of the logo from the left
+ --wrap-lines [] Wrap lines when printing in terminal
+ --logo-padding-top Padding of the logo from the top
+ --logo-padding-left Padding of the logo from the left
--layout-padding-top Padding of the layout from the top
- --title-sep A char (or string) to use in $
- --sep-reset A separator (or string) that when ecountered, will automatically reset color
- --sep-reset-after [] Reset color either before of after 'sep-reset' (1 = after && 0 = before)
+ --title-sep A character (or string) to use in $
+ --sep-reset A character (or string) that when encountered, will automatically reset color
+ --sep-reset-after [] Reset color either before or after 'sep-reset'
--gen-config [] Generate default config file to config folder (if path, it will generate to the path)
Will ask for confirmation if file exists already
- --color Replace instances of a color with another value.
- Syntax MUST be "name=value" with no space beetween "=", example: --color "foo=#444333".
- Thus replaces any instance of foo with #444333. Can be done with multiple colors separetly.
+ --add-color Replace instances of a color with another value.
+ Syntax MUST be "name=value" with no space between "=", example: --color "foo=#444333".
+ Thus replaces any instance of foo with #444333. Can be done with multiple colors separately.
Read the manual "customfetch.1" or the autogenerated config file for more infos about customfetch and how it works
-)"sv);
+)");
+
+ RETURN_OR_PRINT(help.data());
std::exit(invalid_opt);
}
-static void modules_list()
+static STRING_IF_ANDROID_APP_ELSE(void) modules_list()
{
- fmt::println(R"(
+ constexpr std::string_view list(R"(
MODULE ONLY
-Should be used in the config as like as $
+Should be used as like as $
Syntax:
# maybe comments of the module
module:
- description [maybe example of what it prints]
+ description [example of what it prints]
ram:
used and total amount of RAM (auto) with used percentage [2.81 GiB / 15.88 GiB (5.34%)]
@@ -157,13 +177,13 @@ disk(/path/to/fs):
GPU shorter vendor name and model name [NVIDIA GeForce GTX 1650]
cpu:
- CPU model name with number of virtual proccessors and max freq [AMD Ryzen 5 5500 (12) @ 4.90 GHz]
+ CPU model name with number of virtual processors and max freq [AMD Ryzen 5 5500 (12) @ 4.90 GHz]
title:
user and hostname colored with ${{auto2}} [toni@arch2]
title_sep:
- separator between the title and the system infos (with the title lenght) [--------]
+ separator between the title and the system infos (with the title length) [--------]
colors:
color palette with background spaces
@@ -171,74 +191,73 @@ disk(/path/to/fs):
colors_light:
light color palette with background spaces
-# with `symb` I mean a symbol to be used for the
+# with `symbol` I mean a symbol to be used for the
# view of the color palette
-colors_symbol(symb):
+colors_symbol(symbol):
color palette with specific symbol
-# with `symb` I mean a symbol to be used for the
+# with `symbol` I mean a symbol to be used for the
# view of the color palette
-colors_light_symbol(symb):
+colors_light_symbol(symbol):
light color palette with specific symbol
MODULE MEMBERS
+Should be used as like as $
+NOTE: there are modules such as "user.de_version" that may slow down customfetch because of querying things like the DE version
+ customfetch is still fast tho :)
Syntax:
# maybe comments of the module
module
- member : description [example of what it prints, maybe another]
-
-Should be used in the config as like as $
-NOTE: there are modules such as "user.de_version" that may slow down customfetch because of querying things like the DE version
- customfetch is still fast tho :)
+ member: description [example of what it prints; maybe another]
os
- name : OS name (pretty name) [Ubuntu 22.04.4 LTS, Arch Linux]
- name_id : OS name id [ubuntu, arch]
- kernel : kernel name and version [Linux 6.9.3-zen1-1-zen]
- kernel_name : kernel name [Linux]
- kernel_version: kernel version [6.9.3-zen1-1-zen]
- version_id : OS version id [22.04.4, 20240101.0.204074]
+ name: OS name (pretty name) [Ubuntu 22.04.4 LTS; Arch Linux]
+ name_id: OS name id [ubuntu, arch]
+ kernel: kernel name and version [Linux 6.9.3-zen1-1-zen]
+ kernel_name: kernel name [Linux]
+ kernel_version: kernel version [6.9.3-zen1-1-zen]
+ version_id: OS version id [22.04.4, 20240101.0.204074]
version_codename: OS version codename [jammy]
- pkgs : the count of the installed packages by a package manager [1869 (pacman), 4 (flatpak)]
- uptime : (auto) uptime of the system [36 mins, 3 hours, 23 days]
- uptime_secs : uptime of the system in seconds (should be used along with others uptime_ members) [45]
- uptime_mins : uptime of the system in minutes (should be used along with others uptime_ members) [12]
- uptime_hours : uptime of the system in hours (should be used along with others uptime_ members) [34]
- uptime_days : uptime of the system in days (should be used along with others uptime_ members) [2]
- hostname : hostname of the OS [mymainPC]
- initsys_name : Init system name [systemd]
- initsys_version: Init system version [256.5-1-arch]
+ pkgs: count of the installed packages by a package manager [1869 (pacman), 4 (flatpak)]
+ uptime: (auto) uptime of the system [36 mins, 3 hours, 23 days]
+ uptime_secs: uptime of the system in seconds (should be used along with others uptime_ members) [45]
+ uptime_mins: uptime of the system in minutes (should be used along with others uptime_ members) [12]
+ uptime_hours: uptime of the system in hours (should be used along with others uptime_ members) [34]
+ uptime_days: uptime of the system in days (should be used along with others uptime_ members) [2]
+ hostname: hostname of the OS [myMainPC]
+ initsys_name: Init system name [systemd]
+ initsys_version: Init system version [256.5-1-arch]
user
- name : name you are currently logged in (not real name) [toni69]
- shell : login shell name and version [zsh 5.9]
- shell_name : login shell [zsh]
- shell_path : login shell (with path) [/bin/zsh]
- shell_version : login shell version (may be not correct) [5.9]
- de_name : Desktop Enviroment current session name [Plasma]
- wm_name : Window manager current session name [dwm, xfwm4]
- wm_version : Window manager version (may not working correctly) [6.2, 4.18.0]
- terminal : Terminal name and version [alacritty 0.13.2]
- terminal_name : Terminal name [alacritty]
- terminal_version: Terminal version [0.13.2]
+ name: name you are currently logged in (not real name) [toni69]
+ shell: login shell name and version [zsh 5.9]
+ shell_name: login shell [zsh]
+ shell_path: login shell (with path) [/bin/zsh]
+ shell_version: login shell version (may be not correct) [5.9]
+ de_name: Desktop Environment current session name [Plasma]
+ wm_name: Window Manager current session name [dwm; xfwm4]
+ wm_version: Window Manager version (may not work correctly) [6.2; 4.18.0]
+ terminal: terminal name and version [alacritty 0.13.2]
+ terminal_name: terminal name [alacritty]
+ terminal_version: terminal version [0.13.2]
# this module is just for generic theme stuff
# such as indeed cursor
# because it is not GTK-Qt specific
theme
- cursor : cursor name with its size (auto add the size if queried) [Bibata-Modern-Ice (16px)]
- cursor_name : cursor name [Bibata-Modern-Ice]
- cursor_size : cursor size [16]
+ cursor: cursor name with its size (auto add the size if queried) [Bibata-Modern-Ice (16px)]
+ cursor_name: cursor name [Bibata-Modern-Ice]
+ cursor_size: cursor size [16]
# If USE_DCONF flag is set, then we're going to use
# dconf, else backing up to gsettings
theme-gsettings
- name : gsettings theme name [Decay-Green]
- icons : gsettings icons theme name [Papirus-Dark]
- font : gsettings font theme name [Cantarell 10]
- cursor : gsettings cursor name with its size (auto add the size if queried) [Bibata-Modern-Ice (16px)]
- cursor_name : gsettings cursor name [Bibata-Modern-Ice]
- cursor_size : gsettings cursor size [16]
+ name: gsettings theme name [Decay-Green]
+ icons: gsettings icons theme name [Papirus-Dark]
+ font: gsettings font theme name [Cantarell 10]
+ cursor: gsettings cursor name with its size (auto add the size if queried) [Bibata-Modern-Ice (16px)]
+ cursor_name: gsettings cursor name [Bibata-Modern-Ice]
+ cursor_size: gsettings cursor size [16]
# the N stands for the gtk version number to query
# so for example if you want to query the gtk3 theme name
@@ -246,18 +265,18 @@ theme-gsettings
# note: may be slow because of calling "gsettings" if couldn't read from configs.
# Read theme-gsettings module comments
theme-gtkN
- name : gtk theme name [Arc-Dark]
- icons : gtk icons theme name [Qogir-Dark]
- font : gtk font theme name [Noto Sans 10]
+ name: gtk theme name [Arc-Dark]
+ icons: gtk icons theme name [Qogir-Dark]
+ font: gtk font theme name [Noto Sans 10]
# basically as like as the "theme-gtkN" module above
# but with gtk{{2,3,4}} and auto format gkt version
# note: may be slow because of calling "gsettings" if couldn't read from configs.
# Read theme-gsettings module comments
theme-gtk-all
- name : gtk theme name [Arc-Dark [GTK2/3/4]]
- icons : gtk icons theme name [Papirus-Dark [GTK2/3], Qogir [GTK4]]
- font : gtk font theme name [Hack Nerd Font 13 [GTK2], Noto Sans 10 [GTK3/4]]
+ name: gtk theme name [Arc-Dark [GTK2/3/4]]
+ icons: gtk icons theme name [Papirus-Dark [GTK2/3], Qogir [GTK4]]
+ font: gtk font theme name [Hack Nerd Font 13 [GTK2], Noto Sans 10 [GTK3/4]]
# note: these members are auto displayed in from B to YB (depending if using SI byte unit or not(IEC)).
# they all (except those that has the same name as the module or that ends with "_perc")
@@ -265,63 +284,74 @@ theme-gtk-all
# example: if you want to show your 512MiB of used RAM in GiB
# use the `used-GiB` variant (they don't print the unit tho)
ram
- used : used amount of RAM (auto) [2.81 GiB]
- free : available amount of RAM (auto) [10.46 GiB]
- total : total amount of RAM (auto) [15.88 GiB]
- used_perc : percentage of used amount of RAM in total [17.69%]
- free_perc : percentage of available amount of RAM in total [82.31%]
+ used: used amount of RAM (auto) [2.81 GiB]
+ free: available amount of RAM (auto) [10.46 GiB]
+ total: total amount of RAM (auto) [15.88 GiB]
+ used_perc: percentage of used amount of RAM in total [17.69%]
+ free_perc: percentage of available amount of RAM in total [82.31%]
# same comments as RAM (above)
swap
- free : available amount of the swapfile (auto) [34.32 MiB]
- total : total amount of the swapfile (auto) [512.00 MiB]
- used : used amount of the swapfile (auto) [477.68 MiB]
- used_perc : percentage of used amount of the swapfile in total [93.29%]
- free_perc : percentage of available amount of the swapfile in total [6.71%]
+ used: used amount of the swapfile (auto) [477.68 MiB]
+ free: available amount of the swapfile (auto) [34.32 MiB]
+ total: total amount of the swapfile (auto) [512.00 MiB]
+ used_perc: percentage of used amount of the swapfile in total [93.29%]
+ free_perc: percentage of available amount of the swapfile in total [6.71%]
# same comments as RAM (above)
# note: the module can have either a device path
# or a filesystem path
# e.g disk(/) or disk(/dev/sda5)
disk(/path/to/fs)
- used : used amount of disk space (auto) [360.02 GiB]
- free : available amount of disk space (auto) [438.08 GiB]
- total : total amount of disk space (auto) [100.08 GiB]
- used_perc : percentage of used amount of the disk in total [82.18%]
- free_perc : percentage of available amount of the disk in total [17.82%]
- fs : type of filesystem [ext4]
- device : path to device [/dev/sda5]
- mountdir : path to the device mount point [/]
+ used: used amount of disk space (auto) [360.02 GiB]
+ free: available amount of disk space (auto) [438.08 GiB]
+ total: total amount of disk space (auto) [100.08 GiB]
+ used_perc: percentage of used amount of the disk in total [82.18%]
+ free_perc: percentage of available amount of the disk in total [17.82%]
+ fs: type of filesystem [ext4]
+ device: path to device [/dev/sda5]
+ mountdir: path to the device mount point [/]
# usually people have 1 GPU in their PC,
# but if you got more than 1 and want to query it,
# you should call gpu module with a number, e.g gpu1 (default gpu0).
# Infos are gotten from `/sys/class/drm/` and on each cardN directory
gpu
- name : GPU model name [GeForce GTX 1650]
- vendor : GPU short vendor name [NVIDIA]
- vendor_long : GPU vendor name [NVIDIA Corporation]
+ name: GPU model name [GeForce GTX 1650]
+ vendor: GPU short vendor name [NVIDIA]
+ vendor_long: GPU vendor name [NVIDIA Corporation]
-# cpu module has a memeber called "temp" and it has 3 variant units:
+# cpu module has a member called "temp" and it has 3 variant units:
# "temp_C" (Celsius) "temp_F" (Fahrenheit) "temp_K" (Kelvin)
cpu
- name : CPU model name [AMD Ryzen 5 5500]
- temp : CPU temperature (by the choosen unit) [40.62]
- nproc : CPU number of virtual proccessors [12]
+ name: CPU model name [AMD Ryzen 5 5500]
+ temp: CPU temperature (by the chosen unit) [40.62]
+ nproc: CPU number of virtual processors [12]
+ freq_cur: CPU freq (current, in GHz) [3.42]
+ freq_min: CPU freq (minimum, in GHz) [2.45]
+ freq_max: CPU freq (maximum, in GHz) [4.90]
freq_bios_limit: CPU freq (limited by bios, in GHz) [4.32]
- freq_cur : CPU freq (current, in GHz) [3.42]
- freq_min : CPU freq (mininum, in GHz) [2.45]
- freq_max : CPU freq (maxinum, in GHz) [4.90]
system
- host : Host (aka. Motherboard) model name with vendor and version [Micro-Star International Co., Ltd. PRO B550M-P GEN3 (MS-7D95) 1.0]
- host_name : Host (aka. Motherboard) model name [PRO B550M-P GEN3 (MS-7D95)]
- host_version : Host (aka. Motherboard) model version [1.0]
- host_vendor : Host (aka. Motherboard) model vendor [Micro-Star International Co., Ltd.]
- arch : the architecture of the machine [x86_64, aarch64]
+ host: Host (aka. Motherboard) model name with vendor and version [Micro-Star International Co., Ltd. PRO B550M-P GEN3 (MS-7D95) 1.0]
+ host_name: Host (aka. Motherboard) model name [PRO B550M-P GEN3 (MS-7D95)]
+ host_version: Host (aka. Motherboard) model version [1.0]
+ host_vendor: Host (aka. Motherboard) model vendor [Micro-Star International Co., Ltd.]
+ arch: the architecture of the machine [x86_64, aarch64]
-)"sv);
+)");
+
+ RETURN_OR_PRINT(list.data());
std::exit(EXIT_SUCCESS);
+
+}
+
+static bool str_to_bool(const std::string_view str)
+{
+ if (str == "true" || str == "1" || str == "enable")
+ return true;
+
+ return false;
}
// clang-format off
@@ -349,7 +379,7 @@ static std::string parse_config_path(int argc, char* argv[], const std::string&
case 'C':
if (!std::filesystem::exists(optarg))
- die("config file '{}' doesn't exist", optarg);
+ die(_("config file '{}' doesn't exist"), optarg);
return optarg;
}
}
@@ -357,7 +387,7 @@ static std::string parse_config_path(int argc, char* argv[], const std::string&
return configDir + "/config.toml";
}
-static bool parseargs(int argc, char* argv[], Config& config, const std::string_view configFile)
+static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config& config, const std::string_view configFile)
{
int opt = 0;
int option_index = 0;
@@ -366,11 +396,11 @@ static bool parseargs(int argc, char* argv[], Config& config, const std::string_
static const struct option opts[] = {
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
- {"no-display", no_argument, 0, 'n'},
{"list-modules", no_argument, 0, 'l'},
- {"logo-only", no_argument, 0, 'L'},
{"gui", no_argument, 0, 'g'},
- {"no-color", no_argument, 0, 'N'},
+ {"logo-only", optional_argument, 0, 'L'},
+ {"no-logo", optional_argument, 0, 'n'},
+ {"no-color", optional_argument, 0, 'N'},
{"ascii-logo-type", optional_argument, 0, 'a'},
{"offset", required_argument, 0, 'o'},
{"font", required_argument, 0, 'f'},
@@ -381,18 +411,17 @@ static bool parseargs(int argc, char* argv[], Config& config, const std::string_
{"distro", required_argument, 0, 'd'},
{"source-path", required_argument, 0, 's'},
{"image-backend", required_argument, 0, 'i'},
-
- {"enable-colors", no_argument, 0, "enable-colors"_fnv1a16},
+
+ {"sep-reset-after", optional_argument, 0, "sep-reset-after"_fnv1a16},
{"wrap-lines", optional_argument, 0, "wrap-lines"_fnv1a16},
+ {"gen-config", optional_argument, 0, "gen-config"_fnv1a16},
{"sep-reset", required_argument, 0, "sep-reset"_fnv1a16},
{"title-sep", required_argument, 0, "title-sep"_fnv1a16},
- {"sep-reset-after", optional_argument, 0, "sep-reset-after"_fnv1a16},
{"logo-padding-top", required_argument, 0, "logo-padding-top"_fnv1a16},
{"logo-padding-left", required_argument, 0, "logo-padding-left"_fnv1a16},
{"layout-padding-top", required_argument, 0, "layout-padding-top"_fnv1a16},
{"bg-image", required_argument, 0, "bg-image"_fnv1a16},
- {"color", required_argument, 0, "color"_fnv1a16},
- {"gen-config", optional_argument, 0, "gen-config"_fnv1a16},
+ {"add-color", required_argument, 0, "add-color"_fnv1a16},
{0,0,0,0}
};
@@ -406,20 +435,16 @@ static bool parseargs(int argc, char* argv[], Config& config, const std::string_
case 0:
break;
case '?':
- help(EXIT_FAILURE); break;
+ RETURN_IF_ANDROID_APP help(EXIT_FAILURE); break;
case 'V':
- version(); break;
+ RETURN_IF_ANDROID_APP version(); break;
case 'h':
- help(); break;
- case 'n':
- config.m_disable_source = true; break;
+ RETURN_IF_ANDROID_APP help(); break;
case 'l':
- modules_list(); break;
+ RETURN_IF_ANDROID_APP modules_list(); break;
case 'f':
config.font = optarg; break;
- case 'L':
- config.m_print_logo_only = true; break;
case 'g':
config.gui = true; break;
case 'o':
@@ -439,13 +464,29 @@ static bool parseargs(int argc, char* argv[], Config& config, const std::string_
case 'i':
config.m_image_backend = optarg; break;
case 'N':
- config.m_disable_colors = true; break;
+ if (OPTIONAL_ARGUMENT_IS_PRESENT)
+ config.m_disable_colors = str_to_bool(optarg);
+ else
+ config.m_disable_colors = true;
+ break;
case 'a':
if (OPTIONAL_ARGUMENT_IS_PRESENT)
config.ascii_logo_type = optarg;
else
config.ascii_logo_type.clear();
break;
+ case 'n':
+ if (OPTIONAL_ARGUMENT_IS_PRESENT)
+ config.m_disable_source = str_to_bool(optarg);
+ else
+ config.m_disable_source = true;
+ break;
+ case 'L':
+ if (OPTIONAL_ARGUMENT_IS_PRESENT)
+ config.m_print_logo_only = str_to_bool(optarg);
+ else
+ config.m_print_logo_only = true;
+ break;
case "logo-padding-top"_fnv1a16:
config.logo_padding_top = std::stoi(optarg); break;
@@ -461,12 +502,12 @@ static bool parseargs(int argc, char* argv[], Config& config, const std::string_
case "wrap-lines"_fnv1a16:
if (OPTIONAL_ARGUMENT_IS_PRESENT)
- config.wrap_lines = static_cast(std::stoi(optarg));
+ config.wrap_lines = str_to_bool(optarg);
else
config.wrap_lines = true;
break;
- case "color"_fnv1a16:
+ case "add-color"_fnv1a16:
config.addAliasColors(optarg); break;
case "gen-config"_fnv1a16:
@@ -482,26 +523,54 @@ static bool parseargs(int argc, char* argv[], Config& config, const std::string_
case "title-sep"_fnv1a16:
config.builtin_title_sep = optarg; break;
- case "enable-colors"_fnv1a16:
- config.m_disable_colors = false; break;
-
case "sep-reset-after"_fnv1a16:
if (OPTIONAL_ARGUMENT_IS_PRESENT)
- config.sep_reset_after = static_cast(std::stoi(optarg));
+ config.sep_reset_after = str_to_bool(optarg);
else
config.sep_reset_after = true;
break;
default:
- return false;
+ return _false;
}
}
- return true;
+ return _true;
}
+static void enable_cursor()
+{
+ fmt::print("\x1B[?25h\x1B[?7h");
+}
+
+/** Sets up gettext localization. Safe to call multiple times.
+ */
+/* Inspired by the monotone function localize_monotone. */
+// taken from pacman
+static void localize(void)
+{
+#if ENABLE_NLS
+ static bool init = false;
+ if (!init)
+ {
+ setlocale(LC_ALL, "");
+ bindtextdomain("customfetch", LOCALEDIR);
+ textdomain("customfetch");
+ init = true;
+ }
+#endif
+}
+
+#if ANDROID_APP
+std::string mainAndroid_and_render(int argc, char *argv[], JNIEnv *env, jobject obj, bool do_not_load_config)
+{
+ jni_objs = {env, obj};
+ // reset option index
+ optind = 0;
+#else
int main(int argc, char *argv[])
{
+#endif
#ifdef VENDOR_TEST
// test
@@ -528,10 +597,23 @@ int main(int argc, char *argv[])
const std::string& configDir = getConfigDir();
const std::string& configFile = parse_config_path(argc, argv, configDir);
- Config config(configFile, configDir, colors);
+ localize();
+#if ANDROID_APP
+ Config config(configFile, configDir, colors, do_not_load_config);
+ const std::string& parseargs_ret = parseargs(argc, argv, config, configFile);
+ if (parseargs_ret != _true)
+ return parseargs_ret;
+
+ // since ANDROID_APP means that it will run as an android widget, so in GUI,
+ // then let's make it always true
+ config.gui = true;
+ config.wrap_lines = true;
+#else
+ Config config(configFile, configDir, colors, false);
if (!parseargs(argc, argv, config, configFile))
return 1;
+#endif // ANDROID_APP
if (config.source_path.empty() || config.source_path == "off")
config.m_disable_source = true;
@@ -557,9 +639,10 @@ int main(int argc, char *argv[])
debug("{} path = {}", __PRETTY_FUNCTION__, path);
if (!std::filesystem::exists(path) && !config.m_disable_source)
- die("'{}' doesn't exist. Can't load image/text file", path);
+ die(_("'{}' doesn't exist. Can't load image/text file"), path);
-#ifdef GUI_MODE
+#if !ANDROID_APP
+#if GUI_MODE
if (config.gui)
{
const auto app = Gtk::Application::create("org.toni.customfetch");
@@ -568,24 +651,31 @@ int main(int argc, char *argv[])
}
#else
if (config.gui)
- die("Can't run in GUI mode because it got disabled at compile time\n"
- "Compile customfetch with GUI_MODE=1 or contact your distro to enable it");
-#endif
+ die(_("Can't run in GUI mode because it got disabled at compile time\n"
+ "Compile customfetch with GUI_MODE=1 or contact your distro to enable it"));
+#endif // GUI_MODE
- if (config.wrap_lines)
+ if (!config.wrap_lines)
{
+ // https://en.cppreference.com/w/c/program/exit
+ // if something goes wrong like a segfault, then re-enable the cursor again
+ std::atexit(enable_cursor);
+
// hide cursor and disable line wrapping
fmt::print("\x1B[?25l\x1B[?7l");
-
+
Display::display(Display::render(config, colors, false, path));
// enable both of them again
- fmt::print("\x1B[?25h\x1B[?7h");
+ enable_cursor();
}
else
{
Display::display(Display::render(config, colors, false, path));
}
+#else
+ return fmt::format("{}", fmt::join(Display::render(config, colors, false, path), " "));
+#endif // !ANDROID_APP
- return 0;
+ return _false; // 0 or "false"
}
diff --git a/src/parse.cpp b/src/parse.cpp
index 70e901cd..94ed99b0 100644
--- a/src/parse.cpp
+++ b/src/parse.cpp
@@ -32,9 +32,11 @@
#include
#include
#include
+#include
#include
#include
#include
+#include
#include
#include "config.hpp"
@@ -114,10 +116,12 @@ static std::array get_ansi_color(const std::string_view str, con
{
const size_t first_m = str.rfind('m');
if (first_m == std::string::npos)
- die("Parser: failed to parse layout/ascii art: missing m while using ANSI color escape code");
+ die(_("Parser: failed to parse layout/ascii art: missing 'm' while using ANSI color escape code in '{}'"), str);
std::string col = str.data();
col.erase(first_m); // 1;42
+
+#if !ANDROID_APP
std::string weight = hasStart(col, "1;") ? "bold" : "normal";
std::string type = "fgcolor"; // either fgcolor or bgcolor
@@ -133,6 +137,17 @@ static std::array get_ansi_color(const std::string_view str, con
if ((n >= 100 && n <= 107) || (n >= 40 && n <= 47))
type = "bgcolor";
+#else
+ std::string weight = hasStart(col, "1;") ? "" : "";
+ std::string type = "color"; // either color or background-color
+
+ if (hasStart(col, "1;") || hasStart(col, "0;"))
+ col.erase(0, 2);
+
+ const int n = std::stoi(col);
+ if ((n >= 100 && n <= 107) || (n >= 40 && n <= 47))
+ type = "background-color";
+#endif // !ANDROID_APP
// last number
// clang-format off
@@ -158,19 +173,19 @@ static std::array get_ansi_color(const std::string_view str, con
static std::string convert_ansi_escape_rgb(const std::string_view noesc_str)
{
if (std::count(noesc_str.begin(), noesc_str.end(), ';') < 4)
- die("ANSI escape code color '\\e[{}' should have an rgb type value\n"
- "e.g \\e[38;2;255;255;255m",
+ die(_("ANSI escape code color '\\e[{}' should have an rgb type value\n"
+ "e.g \\e[38;2;255;255;255m"),
noesc_str);
if (noesc_str.rfind('m') == std::string::npos)
- die("Parser: failed to parse layout/ascii art: missing m while using ANSI color escape code");
+ die(_("Parser: failed to parse layout/ascii art: missing m while using ANSI color escape code"));
const std::vector& rgb_str = split(noesc_str.substr(5), ';');
const uint r = std::stoul(rgb_str.at(0));
const uint g = std::stoul(rgb_str.at(1));
const uint b = std::stoul(rgb_str.at(2));
-
- const uint result = (r << 16) | (g << 8) | (b);
+ const uint result = (r << 16) | (g << 8) | (b);
+
std::stringstream ss;
ss << std::hex << result;
return ss.str();
@@ -178,19 +193,19 @@ static std::string convert_ansi_escape_rgb(const std::string_view noesc_str)
std::string parse(const std::string_view input, std::string& _, parse_args_t& parse_args)
{
- return parse(input.data(), parse_args.systemInfo, _, parse_args.config, parse_args.colors, parse_args.parsingLayout);
+ return parse(input.data(), parse_args.systemInfo, _, parse_args.config, parse_args.colors, parse_args.parsingLayout, parse_args.no_more_reset);
}
std::string parse(const std::string_view input, parse_args_t& parse_args)
{
- return parse(input.data(), parse_args.systemInfo, parse_args.pureOutput, parse_args.config, parse_args.colors, parse_args.parsingLayout);
+ return parse(input.data(), parse_args.systemInfo, parse_args.pureOutput, parse_args.config, parse_args.colors, parse_args.parsingLayout, parse_args.no_more_reset);
}
static std::string get_and_color_percentage(const float& n1, const float& n2, parse_args_t& parse_args,
const bool invert = false)
{
const Config& config = parse_args.config;
- const float result = static_cast(n1 / n2 * static_cast(100));
+ const float result = n1 / n2 * static_cast(100);
std::string color;
if (!invert)
@@ -286,14 +301,25 @@ std::optional parse_color_tag(Parser& parser, parse_args_t& parse_a
if (!evaluate)
return {};
-
- static std::vector auto_colors;
- std::string output;
- const Config& config = parse_args.config;
- const colors_t& colors = parse_args.colors;
- const size_t taglen = color.length() + "${}"_len;
- const std::string endspan = (!parse_args.firstrun_clr ? "" : "");
+ std::string output;
+ const Config& config = parse_args.config;
+ const colors_t& colors = parse_args.colors;
+ const size_t taglen = color.length() + "${}"_len;
+
+#if ANDROID_APP
+ std::string& endspan = parse_args.endspan;
+ output += endspan;
+ endspan.clear();
+
+ const auto& append_endspan = [&endspan](const std::string_view tag) {
+ endspan += "";
+ endspan += tag;
+ endspan += ">";
+ };
+#else
+ const std::string endspan = (!parse_args.firstrun_clr ? "" : "");
+#endif
if (config.m_disable_colors)
{
@@ -341,6 +367,7 @@ std::optional parse_color_tag(Parser& parser, parse_args_t& parse_a
}
jumpauto:
+#if !ANDROID_APP
if (color == "1")
{
output += config.gui ? endspan + "" : NOCOLOR_BOLD;
@@ -379,7 +406,7 @@ std::optional parse_color_tag(Parser& parser, parse_args_t& parse_a
{
const size_t closebrak = opt_clr.find(')', argmode_pos);
if (closebrak == std::string::npos)
- die("{} mode in color {} doesn't have close bracket", error, str_clr);
+ die(_("{} mode in color {} doesn't have close bracket"), error, str_clr);
const std::string& value = opt_clr.substr(argmode_pos + 2, closebrak - argmode_pos - 2);
tagfmt += fmt.data() + value + "' ";
@@ -464,6 +491,10 @@ std::optional parse_color_tag(Parser& parser, parse_args_t& parse_a
const std::string& hexclr = convert_ansi_escape_rgb(noesc_str);
output += fmt::format("{}", endspan, hasStart(noesc_str, "38") ? 'f' : 'b', hexclr);
}
+ else if (hasStart(noesc_str, "38;5;") || hasStart(noesc_str, "48;5;"))
+ {
+ die(_("256 true color '{}' works only in terminal"), noesc_str);
+ }
else
{
const std::array& clrs = get_ansi_color(noesc_str, colors);
@@ -476,7 +507,7 @@ std::optional parse_color_tag(Parser& parser, parse_args_t& parse_a
else
{
- error("PARSER: failed to parse line with color '{}'", str_clr);
+ error(_("PARSER: failed to parse line with color '{}'"), str_clr);
if (!parse_args.parsingLayout && parser.dollar_pos != std::string::npos)
parse_args.pureOutput.erase(parser.dollar_pos, taglen);
return output;
@@ -570,17 +601,207 @@ std::optional parse_color_tag(Parser& parser, parse_args_t& parse_a
else
{
- error("PARSER: failed to parse line with color '{}'", str_clr);
+ error(_("PARSER: failed to parse line with color '{}'"), str_clr);
if (!parse_args.parsingLayout && parser.dollar_pos != std::string::npos)
parse_args.pureOutput.erase(parser.dollar_pos, taglen);
return output;
}
}
+ if (!parse_args.parsingLayout &&
+ std::find(auto_colors.begin(), auto_colors.end(), color) == auto_colors.end())
+ auto_colors.push_back(color);
+ }
+#else
+ if (color == "1")
+ {
+ output += "";
+ append_endspan("b");
+ }
+ else if (color == "0")
+ {
+ output += "";
+ append_endspan("span");
+ }
+ else
+ {
+ std::string str_clr;
+ switch (fnv1a16::hash(color))
+ {
+ case "black"_fnv1a16: str_clr = colors.gui_black; break;
+ case "red"_fnv1a16: str_clr = colors.gui_red; break;
+ case "blue"_fnv1a16: str_clr = colors.gui_blue; break;
+ case "green"_fnv1a16: str_clr = colors.gui_green; break;
+ case "cyan"_fnv1a16: str_clr = colors.gui_cyan; break;
+ case "yellow"_fnv1a16: str_clr = colors.gui_yellow; break;
+ case "magenta"_fnv1a16: str_clr = colors.gui_magenta; break;
+ case "white"_fnv1a16: str_clr = colors.gui_white; break;
+ default: str_clr = color; break;
+ }
+
+ const size_t pos = str_clr.rfind('#');
+ if (pos != std::string::npos)
+ {
+ std::string tagfmt;
+ const std::string& opt_clr = str_clr.substr(0, pos);
+ const std::string& hexclr = str_clr.substr(pos + 1);
+
+ size_t argmode_pos = 0;
+ /*const auto& get_argmode_value = [&](const std::string_view error) -> std::string {
+ if (opt_clr.at(argmode_pos + 1) == '(')
+ {
+ const size_t closebrak = opt_clr.find(')', argmode_pos);
+ if (closebrak == std::string::npos)
+ die(_("{} mode in tag color {} doesn't have close bracket"), error, str_clr);
+
+ const std::string& value = opt_clr.substr(argmode_pos + 2, closebrak - argmode_pos - 2);
+ return value;
+ }
+ return "";
+ };*/
+
+ const auto& append_argmode = [&](const std::string_view fmt, const std::string_view error) -> size_t {
+ if (opt_clr.at(argmode_pos + 1) == '(')
+ {
+ const size_t closebrak = opt_clr.find(')', argmode_pos);
+ if (closebrak == std::string::npos)
+ die(_("{} mode in tag color {} doesn't have close bracket"), error, str_clr);
+
+ const std::string& value = opt_clr.substr(argmode_pos + 2, closebrak - argmode_pos - 2);
+ tagfmt += fmt.data() + value + ";\">";
+
+ return closebrak;
+ }
+ return 0;
+ };
+
+ const auto& skip_gui_argmode = [&](const size_t index) -> size_t {
+ if (opt_clr.at(index + 1) == '(')
+ {
+ const size_t closebrak = opt_clr.find(')', index);
+ if (closebrak == std::string::npos)
+ return 0;
+
+ return closebrak;
+ }
+ return 0;
+ };
+
+ bool bgcolor = false;
+ for (size_t i = 0; i < opt_clr.length(); ++i)
+ {
+ switch (opt_clr.at(i))
+ {
+ case 'b':
+ bgcolor = true;
+ tagfmt += "";
+ break;
+ case '!': tagfmt += ""; append_endspan("b"); break;
+ case 'u': tagfmt += ""; append_endspan("u"); break;
+ case 'i': tagfmt += "