diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index bfcc3bc8..e3be746d 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -6,6 +6,10 @@ on: - '**/*.sh' workflow_dispatch: +permissions: + contents: read + pull-requests: write + jobs: shellcheck: name: Shellcheck diff --git a/Cargo.lock b/Cargo.lock index 86b9ef04..07123221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,60 +37,16 @@ checksum = "67555e1f1ece39d737e28c8a017721287753af3f93225e4a445b29ccb0f5912c" dependencies = [ "nom", "ratatui", - "simdutf8", "smallvec", "thiserror", ] -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstyle" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys", -] - [[package]] name = "anyhow" version = "1.0.88" @@ -157,6 +113,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clap" version = "4.5.20" @@ -173,10 +135,8 @@ version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ - "anstream", "anstyle", "clap_lex", - "strsim", ] [[package]] @@ -197,12 +157,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - [[package]] name = "compact_str" version = "0.8.0" @@ -317,6 +271,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + [[package]] name = "heck" version = "0.5.0" @@ -359,12 +319,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.1", ] [[package]] @@ -392,12 +352,6 @@ dependencies = [ "libc", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itertools" version = "0.13.0" @@ -445,6 +399,7 @@ dependencies = [ "anstyle", "clap", "linutil_core", + "nix 0.29.0", "oneshot", "portable-pty", "rand", @@ -487,7 +442,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -538,6 +493,18 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -623,7 +590,7 @@ dependencies = [ "lazy_static", "libc", "log", - "nix", + "nix 0.25.1", "serial", "shared_library", "shell-words", @@ -717,9 +684,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -907,24 +874,12 @@ dependencies = [ "libc", ] -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "static_assertions" version = "1.1.0" @@ -937,12 +892,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "strum" version = "0.26.3" @@ -996,11 +945,6 @@ name = "textwrap" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width 0.1.14", -] [[package]] name = "thiserror" @@ -1078,9 +1022,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", @@ -1147,12 +1091,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -1341,9 +1279,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/README.md b/README.md index 867d16ce..cd7149f2 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,16 @@ curl -fsSL https://christitus.com/linux | sh ```bash curl -fsSL https://christitus.com/linuxdev | sh ``` -
- CLI arguments + +### CLI arguments Linutil supports various command-line arguments to customize its behavior. Here are some common arguments you can use: -- `-t, --theme ` : Set the theme to use in the application [default: default] [possible values: default, compatible]. +- `-c, --config ` : Path to the configuration file. - `--override-validation` : Show all available options, disregarding compatibility checks (UNSAFE). +- `--size-bypass` : Bypass the terminal size limit. +- `-y, --skip-confirmation` : Skip confirmation prompt before executing commands. +- `-t, --theme ` : Set the theme to use in the application [default: `default`] [possible values: `default`, `compatible`]. - `-h, --help` : Print help. For more detailed usage, run: @@ -42,7 +45,6 @@ curl -fsSL https://christitus.com/linux | sh -s -- --help ```bash linutil --help ``` -
## ⬇️ Installation diff --git a/core/tabs/applications-setup/auto-cpufreq.sh b/core/tabs/applications-setup/auto-cpufreq.sh new file mode 100644 index 00000000..b90744ca --- /dev/null +++ b/core/tabs/applications-setup/auto-cpufreq.sh @@ -0,0 +1,111 @@ +#!/bin/sh -e + +. ../common-script.sh + +installAutoCpufreq() { + clear + + AUTO_CPUFREQ_PATH="$HOME/.local/share/auto-cpufreq" + + if ! command_exists auto-cpufreq; then + printf "%b\n" "${YELLOW}Installing auto-cpufreq.${RC}" + + if ! command_exists git && [ "$PACKAGER" != "pacman" ]; then + printf "%b\n" "${YELLOW}Installing git.${RC}" + case "$PACKAGER" in + *) + "$ESCALATION_TOOL" "$PACKAGER" install -y git + ;; + esac + fi + + case "$PACKAGER" in + pacman) + if command_exists powerprofilesctl; then + printf "%b\n" "${YELLOW}Disabling powerprofilesctl service.${RC}" + "$ESCALATION_TOOL" systemctl disable --now power-profiles-daemon + fi + + "$AUR_HELPER" -S --needed --noconfirm auto-cpufreq + "$ESCALATION_TOOL" systemctl enable --now auto-cpufreq + ;; + *) + mkdir -p "$HOME/.local/share" + + if [ -d "$AUTO_CPUFREQ_PATH" ]; then + rm -rf "$AUTO_CPUFREQ_PATH" + fi + + printf "%b\n" "${YELLOW}Cloning auto-cpufreq repository.${RC}" + git clone --depth=1 https://github.com/AdnanHodzic/auto-cpufreq.git "$AUTO_CPUFREQ_PATH" + + cd "$AUTO_CPUFREQ_PATH" + printf "%b\n" "${YELLOW}Running auto-cpufreq installer.${RC}" + "$ESCALATION_TOOL" ./auto-cpufreq-installer + "$ESCALATION_TOOL" auto-cpufreq --install + ;; + esac + else + printf "%b\n" "${GREEN}auto-cpufreq is already installed.${RC}" + fi +} + +applyTweak() { + printf "%b\n" "${YELLOW}Configuring auto-cpufreq.${RC}" + + if command_exists auto-cpufreq; then + if ls /sys/class/power_supply/BAT* >/dev/null 2>&1; then + printf "%b\n" "${GREEN}System detected as laptop. Updating auto-cpufreq for laptop.${RC}" + "$ESCALATION_TOOL" auto-cpufreq --force powersave + else + printf "%b\n" "${GREEN}System detected as desktop. Updating auto-cpufreq for desktop.${RC}" + "$ESCALATION_TOOL" auto-cpufreq --force performance + fi + else + printf "%b\n" "${RED}auto-cpufreq is not installed.${RC}" + exit 1 + fi +} + +removeTweak() { + if command_exists auto-cpufreq; then + printf "%b\n" "${YELLOW}Removing auto-cpufreq tweak.${RC}" + "$ESCALATION_TOOL" auto-cpufreq --force reset + else + printf "%b\n" "${RED}auto-cpufreq is not installed.${RC}" + exit 1 + fi +} + +main() { + printf "%b\n" "${YELLOW}Do you want to apply the auto-cpufreq tweak or remove it?${RC}" + printf "%b\n" "${YELLOW}1) Apply tweak${RC}" + printf "%b\n" "${YELLOW}2) Remove tweak${RC}" + printf "%b\n" "${YELLOW}3) Exit${RC}" + printf "%b" "Enter your choice [1/3]: " + read -r choice + + case "$choice" in + 1) + applyTweak + ;; + 2) + removeTweak + ;; + 3) + printf "%b\n" "${GREEN}Exiting.${RC}" + exit 0 + ;; + *) + printf "%b\n" "${RED}Invalid choice. Exiting.${RC}" + exit 1 + ;; + esac + + printf "%b\n" "${GREEN}auto-cpufreq setup complete.${RC}" +} + +checkEnv +checkEscalationTool +installAutoCpufreq +main diff --git a/core/tabs/applications-setup/browsers/brave.sh b/core/tabs/applications-setup/browsers/brave.sh index 6618f0fe..995775e3 100644 --- a/core/tabs/applications-setup/browsers/brave.sh +++ b/core/tabs/applications-setup/browsers/brave.sh @@ -25,7 +25,12 @@ installBrave() { ;; dnf) "$ESCALATION_TOOL" "$PACKAGER" install -y dnf-plugins-core - "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo + dnf_version=$(dnf --version | head -n 1 | cut -d '.' -f 1) + if [ "$dnf_version" -eq 4 ]; then + "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo + else + "$ESCALATION_TOOL" "$PACKAGER" config-manager addrepo --from-repofile=https://brave-browser-rpm-release.s3.brave.com/brave-browser.repo + fi "$ESCALATION_TOOL" rpm --import https://brave-browser-rpm-release.s3.brave.com/brave-core.asc "$ESCALATION_TOOL" "$PACKAGER" install -y brave-browser ;; diff --git a/core/tabs/applications-setup/browsers/vivaldi.sh b/core/tabs/applications-setup/browsers/vivaldi.sh index 311816d0..28535a52 100644 --- a/core/tabs/applications-setup/browsers/vivaldi.sh +++ b/core/tabs/applications-setup/browsers/vivaldi.sh @@ -15,7 +15,12 @@ installVivaldi() { ;; dnf) "$ESCALATION_TOOL" "$PACKAGER" install -y dnf-plugins-core - "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://repo.vivaldi.com/stable/vivaldi-fedora.repo + dnf_version=$(dnf --version | head -n 1 | cut -d '.' -f 1) + if [ "$dnf_version" -eq 4 ]; then + "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://repo.vivaldi.com/stable/vivaldi-fedora.repo + else + "$ESCALATION_TOOL" "$PACKAGER" config-manager addrepo --from-repofile=https://repo.vivaldi.com/stable/vivaldi-fedora.repo + fi "$ESCALATION_TOOL" "$PACKAGER" install -y vivaldi-stable ;; zypper) diff --git a/core/tabs/applications-setup/developer-tools/ngrok.sh b/core/tabs/applications-setup/developer-tools/ngrok.sh index 1ba6ff66..a45d7686 100644 --- a/core/tabs/applications-setup/developer-tools/ngrok.sh +++ b/core/tabs/applications-setup/developer-tools/ngrok.sh @@ -4,8 +4,17 @@ installNgrok() { if ! command_exists ngrok; then - printf "%b\n" "${YELLOW}Installing Ngrok...${RC}" - curl -sSLO https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz | "$ESCALATION_TOOL" tar -xz -C /usr/local/bin + printf "%b\n" "${YELLOW}Installing Ngrok.${RC}" + case "$ARCH" in + x86_64) + url="https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz" + ;; + aarch64) + url="https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-arm64.tgz" + ;; + esac + curl -sSL "$url" -o ngrok.tgz + "$ESCALATION_TOOL" tar -xzf ngrok.tgz -C /usr/local/bin else printf "%b\n" "${GREEN}Ngrok is already installed.${RC}" fi @@ -13,4 +22,4 @@ installNgrok() { checkEnv checkEscalationTool -installNgrok \ No newline at end of file +installNgrok diff --git a/core/tabs/applications-setup/developer-tools/sublime.sh b/core/tabs/applications-setup/developer-tools/sublime.sh index 16c03497..18843752 100644 --- a/core/tabs/applications-setup/developer-tools/sublime.sh +++ b/core/tabs/applications-setup/developer-tools/sublime.sh @@ -23,7 +23,12 @@ installSublime() { ;; dnf) "$ESCALATION_TOOL" rpm -v --import https://download.sublimetext.com/sublimehq-rpm-pub.gpg - "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://download.sublimetext.com/rpm/stable/x86_64/sublime-text.repo + dnf_version=$(dnf --version | head -n 1 | cut -d '.' -f 1) + if [ "$dnf_version" -eq 4 ]; then + "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://download.sublimetext.com/rpm/dev/x86_64/sublime-text.repo + else + "$ESCALATION_TOOL" "$PACKAGER" config-manager addrepo --from-repofile=https://download.sublimetext.com/rpm/dev/x86_64/sublime-text.repo + fi "$ESCALATION_TOOL" "$PACKAGER" install -y sublime-text ;; *) diff --git a/core/tabs/applications-setup/docker-setup.sh b/core/tabs/applications-setup/docker-setup.sh index c4fc53ff..b693656b 100755 --- a/core/tabs/applications-setup/docker-setup.sh +++ b/core/tabs/applications-setup/docker-setup.sh @@ -28,7 +28,12 @@ install_docker() { ;; dnf) "$ESCALATION_TOOL" "$PACKAGER" -y install dnf-plugins-core - "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo + dnf_version=$(dnf --version | head -n 1 | cut -d '.' -f 1) + if [ "$dnf_version" -eq 4 ]; then + "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo + else + "$ESCALATION_TOOL" "$PACKAGER" config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo + fi "$ESCALATION_TOOL" "$PACKAGER" -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin "$ESCALATION_TOOL" systemctl enable --now docker ;; @@ -58,7 +63,12 @@ install_docker_compose() { ;; dnf) "$ESCALATION_TOOL" "$PACKAGER" -y install dnf-plugins-core - "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo + dnf_version=$(dnf --version | head -n 1 | cut -d '.' -f 1) + if [ "$dnf_version" -eq 4 ]; then + "$ESCALATION_TOOL" "$PACKAGER" config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo + else + "$ESCALATION_TOOL" "$PACKAGER" config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo + fi "$ESCALATION_TOOL" "$PACKAGER" install -y docker-compose-plugin ;; zypper) diff --git a/core/tabs/applications-setup/tab_data.toml b/core/tabs/applications-setup/tab_data.toml index 3d01ca5f..837e0178 100644 --- a/core/tabs/applications-setup/tab_data.toml +++ b/core/tabs/applications-setup/tab_data.toml @@ -233,6 +233,12 @@ description = "Universal Android Debloater (UAD) is a tool designed to help user script = "android-debloat.sh" task_list = "I" +[[data]] +name = "Auto CPU Frequency" +description = "Automatic CPU speed & power optimizer.\nhttps://github.com/AdnanHodzic/auto-cpufreq" +script = "auto-cpufreq.sh" +task_list = "I SS" + [[data]] name = "Bash Prompt" description = "The .bashrc file is a script that runs every time a new terminal session is started in Unix-like operating systems. It is used to configure the shell session, set up aliases, define functions, and more, making the terminal easier to use and more powerful. This command configures the key sections and functionalities defined in the .bashrc file from CTT's mybash repository. https://github.com/ChrisTitusTech/mybash" diff --git a/core/tabs/applications-setup/waydroid-setup.sh b/core/tabs/applications-setup/waydroid-setup.sh index 8cac399f..0d305e65 100755 --- a/core/tabs/applications-setup/waydroid-setup.sh +++ b/core/tabs/applications-setup/waydroid-setup.sh @@ -3,7 +3,7 @@ . ../common-script.sh checkGpu() { - if lspci | grep -i nvidia > /dev/null; then + if lspci | grep -i nvidia >/dev/null; then printf "%b\n" "${RED}Waydroid is not compatible with NVIDIA GPUs.${RC}" exit 1 fi @@ -11,28 +11,31 @@ checkGpu() { installWaydroid() { if ! command_exists waydroid; then - printf "%b\n" "${YELLOW}Installing Waydroid...${RC}" + printf "%b\n" "${YELLOW}Installing Waydroid...${RC}" case "$PACKAGER" in pacman) "$AUR_HELPER" -S --needed --noconfirm waydroid + if ! command_exists dkms; then - installed_kernels=$("$PACKAGER" -Q | grep -E '^linux(| |-rt|-rt-lts|-hardened|-zen|-lts)[^-headers]' | cut -d ' ' -f 1) - for kernel in $installed_kernels; do - header="${kernel}-headers" - printf "%b\n" "${CYAN}Installing headers for $kernel...${RC}" - "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm "$header" - done "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm dkms fi + + installed_kernels=$("$PACKAGER" -Q | grep -E '^linux(| |-rt|-rt-lts|-hardened|-zen|-lts)[^-headers]' | cut -d ' ' -f 1) + for kernel in $installed_kernels; do + header="${kernel}-headers" + printf "%b\n" "${CYAN}Installing headers for $kernel...${RC}" + "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm "$header" + done + "$AUR_HELPER" -S --needed --noconfirm binder_linux-dkms "$ESCALATION_TOOL" modprobe binder-linux device=binder,hwbinder,vndbinder ;; - apt-get|nala) + apt-get | nala) curl https://repo.waydro.id | "$ESCALATION_TOOL" sh "$ESCALATION_TOOL" "$PACKAGER" install -y waydroid if command_exists dkms; then "$ESCALATION_TOOL" "$PACKAGER" install -y git - mkdir -p "$HOME/.local/share/" # only create it if it doesnt exist + mkdir -p "$HOME/.local/share/" git clone https://github.com/choff/anbox-modules.git "$HOME/.local/share/anbox-modules" cd "$HOME/.local/share/anbox-modules" "$ESCALATION_TOOL" cp anbox.conf /etc/modules-load.d/ @@ -47,7 +50,7 @@ installWaydroid() { "$ESCALATION_TOOL" "$PACKAGER" install -y waydroid if command_exists dkms; then "$ESCALATION_TOOL" "$PACKAGER" install -y git - mkdir -p "$HOME/.local/share/" # only create it if it doesnt exist + mkdir -p "$HOME/.local/share/" git clone https://github.com/choff/anbox-modules.git "$HOME/.local/share/anbox-modules" cd "$HOME/.local/share/anbox-modules" "$ESCALATION_TOOL" cp anbox.conf /etc/modules-load.d/ diff --git a/core/tabs/common-script.sh b/core/tabs/common-script.sh index f8188ac3..c3472c08 100644 --- a/core/tabs/common-script.sh +++ b/core/tabs/common-script.sh @@ -86,6 +86,13 @@ checkAURHelper() { checkEscalationTool() { ## Check for escalation tools. if [ -z "$ESCALATION_TOOL_CHECKED" ]; then + if [ "$(id -u)" = "0" ]; then + ESCALATION_TOOL="eval" + ESCALATION_TOOL_CHECKED=true + printf "%b\n" "${CYAN}Running as root, no escalation needed${RC}" + return 0 + fi + ESCALATION_TOOLS='sudo doas' for tool in ${ESCALATION_TOOLS}; do if command_exists "${tool}"; then diff --git a/core/tabs/utils/power-profile.sh b/core/tabs/utils/power-profile.sh deleted file mode 100644 index 9c242c4d..00000000 --- a/core/tabs/utils/power-profile.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/sh -e - -. ../common-script.sh - -installAutoCpufreq() { - clear - printf "%b\n" "${YELLOW}Checking if auto-cpufreq is already installed...${RC}" - - # Check if auto-cpufreq is already installed - if command_exists auto-cpufreq; then - printf "%b\n" "${GREEN}auto-cpufreq is already installed.${RC}" - else - printf "%b\n" "${YELLOW}Installing auto-cpufreq...${RC}" - - # Install git if not already installed - if ! command_exists git; then - printf "%b\n" "${YELLOW}git not found. Installing git...${RC}" - case "$PACKAGER" in - pacman) - "$ESCALATION_TOOL" "$PACKAGER" -S --needed --noconfirm git - ;; - apk) - "$ESCALATION_TOOL" "$PACKAGER" add git - ;; - *) - "$ESCALATION_TOOL" "$PACKAGER" install -y git - ;; - esac - fi - - # Clone the auto-cpufreq repository and run the installer - if [ ! -d "auto-cpufreq" ]; then - printf "%b\n" "${YELLOW}Cloning auto-cpufreq repository...${RC}" - git clone https://github.com/AdnanHodzic/auto-cpufreq.git - fi - - cd auto-cpufreq - printf "%b\n" "${YELLOW}Running auto-cpufreq installer...${RC}" - "$ESCALATION_TOOL" ./auto-cpufreq-installer - "$ESCALATION_TOOL" auto-cpufreq --install - - cd .. - fi -} - -configureAutoCpufreq() { - printf "%b\n" "${YELLOW}Configuring auto-cpufreq...${RC}" - - if command_exists auto-cpufreq; then - # Check if the system has a battery to determine if it's a laptop - if ls /sys/class/power_supply/BAT* >/dev/null 2>&1; then - printf "%b\n" "${GREEN}System detected as laptop. Updating auto-cpufreq for laptop...${RC}" - "$ESCALATION_TOOL" auto-cpufreq --force powersave - else - printf "%b\n" "${GREEN}System detected as desktop. Updating auto-cpufreq for desktop...${RC}" - "$ESCALATION_TOOL" auto-cpufreq --force performance - fi - else - printf "%b\n" "${RED}auto-cpufreq is not installed, skipping configuration.${RC}" - fi -} - -removeAutoCpufreqTweak() { - printf "%b\n" "${YELLOW}Removing auto-cpufreq tweak...${RC}" - - if command_exists auto-cpufreq; then - printf "%b\n" "${YELLOW}Resetting auto-cpufreq configuration...${RC}" - "$ESCALATION_TOOL" auto-cpufreq --force reset - else - printf "%b\n" "${RED}auto-cpufreq is not installed, skipping removal.${RC}" - fi -} - -apply_or_remove_auto_cpufreq() { - # Prompt user for action - printf "%b\n" "${YELLOW}Do you want to apply the auto-cpufreq tweak or remove it?${RC}" - printf "%b\n" "${YELLOW}1) Apply tweak${RC}" - printf "%b\n" "${YELLOW}2) Remove tweak${RC}" - printf "%b" "Enter your choice [1/2]: " - read -r choice - - case $choice in - 1) - configureAutoCpufreq - ;; - 2) - removeAutoCpufreqTweak - ;; - *) - printf "%b\n" "${RED}Invalid choice. Exiting.${RC}" - exit 1 - ;; - esac - - printf "%b\n" "${GREEN}auto-cpufreq setup complete.${RC}" -} - -checkEnv -checkEscalationTool -installAutoCpufreq -apply_or_remove_auto_cpufreq diff --git a/core/tabs/utils/tab_data.toml b/core/tabs/utils/tab_data.toml index 7afc2e27..034d8749 100644 --- a/core/tabs/utils/tab_data.toml +++ b/core/tabs/utils/tab_data.toml @@ -127,11 +127,6 @@ name = "Auto Login" script = "auto-login.sh" task_list = "DE setup" -[[data]] -name = "Auto Power Profiling" -script = "power-profile.sh" -task_list = "I" - [[data]] name = "Bluetooth Manager" description = "This utility is designed to manage bluetooth in your system" diff --git a/man/linutil.1 b/man/linutil.1 index f846614f..c66c0019 100644 --- a/man/linutil.1 +++ b/man/linutil.1 @@ -1,5 +1,5 @@ .\" manpage for Linutil -.TH "linutil" "1" "October 2024" "" "Linutil Manual" +.TH "linutil" "1" "November 2024" "Linutil 2024.10.31" "Linutil Manual" .SH NAME linutil \- Chris Titus Tech's Linux Toolbox @@ -26,6 +26,18 @@ curl -fsSL https://christitus.com/linuxdev | sh \fB\-c\fR, \fB\-\-config\fR \fI\fR Path to the configuration file. +.TP +\fB\-\-override\-validation\fR +Show all available entries, disregarding compatibility checks (\fBUNSAFE\fR). + +.TP +\fB\-\-size\-bypass\fR +Bypass the terminal size limit. + +.TP +\fB\-y\fR, \fB\-\-skip\-confirmation\fR +Skip confirmation prompt before executing commands. + .TP \fB\-t\fR, \fB\-\-theme\fR \fI\fR Set the theme to use in the TUI. @@ -36,18 +48,6 @@ Possible values: .br Defaults to \fIdefault\fR. -.TP -\fB\-y\fR, \fB\-\-skip\-confirmation\fR -Skip confirmation prompt before executing commands. - -.TP -\fB\-\-override\-validation\fR -Show all available entries, disregarding compatibility checks. (\fBUNSAFE\fR) - -.TP -\fB\-\-size\-bypass\fR -Bypass the terminal size limit - .TP \fB\-h\fR, \fB\-\-help\fR Print help. diff --git a/tui/Cargo.toml b/tui/Cargo.toml index 4051d3b3..5094b815 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -14,22 +14,23 @@ default = ["tips"] tips = ["rand"] [dependencies] -clap = { version = "4.5.20", features = ["derive"] } -oneshot = "0.1.8" +clap = { version = "4.5.20", features = ["derive", "std"], default-features = false } +oneshot = { version = "0.1.8", features = ["std"], default-features = false } portable-pty = "0.8.1" -ratatui = "0.29.0" +ratatui = { version = "0.29.0", features = ["crossterm"], default-features = false } tui-term = "0.2.0" temp-dir = "0.1.14" -time = { version = "0.3.36", features = ["local-offset", "macros", "formatting"] } -unicode-width = "0.2.0" +time = { version = "0.3.36", features = ["formatting", "local-offset", "macros"], default-features = false } +unicode-width = { version = "0.2.0", default-features = false } rand = { version = "0.8.5", optional = true } -linutil_core = { path = "../core", version = "24.9.28" } +linutil_core = { version = "24.9.28", path = "../core" } tree-sitter-highlight = "0.24.3" tree-sitter-bash = "0.23.1" -textwrap = "0.16.1" -anstyle = "1.0.8" -ansi-to-tui = "7.0.0" +textwrap = { version = "0.16.1", default-features = false } +anstyle = { version = "1.0.8", default-features = false } +ansi-to-tui = { version = "7.0.0", default-features = false } zips = "0.1.7" +nix = { version = "0.29.0", features = [ "user" ] } [[bin]] name = "linutil" diff --git a/tui/src/confirmation.rs b/tui/src/confirmation.rs index 96ab06ca..2ed5898b 100644 --- a/tui/src/confirmation.rs +++ b/tui/src/confirmation.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use crate::{float::FloatContent, hint::Shortcut}; use ratatui::{ - crossterm::event::{KeyCode, KeyEvent}, + crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind}, layout::Alignment, prelude::*, widgets::{Block, Borders, Clear, List}, @@ -85,6 +85,19 @@ impl FloatContent for ConfirmPrompt { frame.render_widget(List::new(paths_text), inner_area); } + fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool { + match event.kind { + MouseEventKind::ScrollDown => { + self.scroll_down(); + } + MouseEventKind::ScrollUp => { + self.scroll_up(); + } + _ => {} + } + false + } + fn handle_key_event(&mut self, key: &KeyEvent) -> bool { use KeyCode::*; self.status = match key.code { diff --git a/tui/src/float.rs b/tui/src/float.rs index 993684b0..4d6ac006 100644 --- a/tui/src/float.rs +++ b/tui/src/float.rs @@ -1,5 +1,5 @@ use ratatui::{ - crossterm::event::{KeyCode, KeyEvent}, + crossterm::event::{KeyCode, KeyEvent, MouseEvent}, layout::{Constraint, Direction, Layout, Rect}, Frame, }; @@ -9,6 +9,7 @@ use crate::hint::Shortcut; pub trait FloatContent { fn draw(&mut self, frame: &mut Frame, area: Rect); fn handle_key_event(&mut self, key: &KeyEvent) -> bool; + fn handle_mouse_event(&mut self, key: &MouseEvent) -> bool; fn is_finished(&self) -> bool; fn get_shortcut_list(&self) -> (&str, Box<[Shortcut]>); } @@ -53,6 +54,10 @@ impl Float { self.content.draw(frame, popup_area); } + pub fn handle_mouse_event(&mut self, event: &MouseEvent) { + self.content.handle_mouse_event(event); + } + // Returns true if the floating window is finished. pub fn handle_key_event(&mut self, key: &KeyEvent) -> bool { match key.code { diff --git a/tui/src/floating_text.rs b/tui/src/floating_text.rs index c307b854..6a2546cc 100644 --- a/tui/src/floating_text.rs +++ b/tui/src/floating_text.rs @@ -9,7 +9,7 @@ use crate::{float::FloatContent, hint::Shortcut}; use linutil_core::Command; use ratatui::{ - crossterm::event::{KeyCode, KeyEvent}, + crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind}, layout::Rect, style::{Style, Stylize}, text::Line, @@ -283,6 +283,17 @@ impl FloatContent for FloatingText { frame.render_widget(list, inner_area); } + fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool { + match event.kind { + MouseEventKind::ScrollDown => self.scroll_down(), + MouseEventKind::ScrollUp => self.scroll_up(), + MouseEventKind::ScrollLeft => self.scroll_left(), + MouseEventKind::ScrollRight => self.scroll_right(), + _ => {} + } + false + } + fn handle_key_event(&mut self, key: &KeyEvent) -> bool { use KeyCode::*; match key.code { diff --git a/tui/src/main.rs b/tui/src/main.rs index 7a9f4067..d391f9e6 100644 --- a/tui/src/main.rs +++ b/tui/src/main.rs @@ -3,6 +3,7 @@ mod filter; mod float; mod floating_text; mod hint; +mod root; mod running_command; pub mod state; mod theme; @@ -19,7 +20,7 @@ use clap::Parser; use ratatui::{ backend::CrosstermBackend, crossterm::{ - event::{self, DisableMouseCapture, Event, KeyEventKind}, + event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyEventKind}, style::ResetColor, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, @@ -63,6 +64,8 @@ fn main() -> io::Result<()> { ); stdout().execute(EnterAlternateScreen)?; + stdout().execute(EnableMouseCapture)?; + enable_raw_mode()?; let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; terminal.clear()?; @@ -92,15 +95,22 @@ fn run( // It's guaranteed that the `read()` won't block when the `poll()` // function returns `true` - if let Event::Key(key) = event::read()? { - // We are only interested in Press and Repeat events - if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { - continue; - } + match event::read()? { + Event::Key(key) => { + if key.kind != KeyEventKind::Press && key.kind != KeyEventKind::Repeat { + continue; + } - if !state.handle_key(&key) { - return Ok(()); + if !state.handle_key(&key) { + return Ok(()); + } } + Event::Mouse(mouse_event) => { + if !state.handle_mouse(&mouse_event) { + return Ok(()); + } + } + _ => {} } } } diff --git a/tui/src/root.rs b/tui/src/root.rs new file mode 100644 index 00000000..1b02b938 --- /dev/null +++ b/tui/src/root.rs @@ -0,0 +1,17 @@ +use crate::floating_text::FloatingText; + +#[cfg(unix)] +use nix::unistd::Uid; + +const ROOT_WARNING: &str = "WARNING: You are running this utility as root!\n +This means you have full system access and commands can potentially damage your system if used incorrectly.\n +Please proceed with caution and make sure you understand what each script does before executing it."; + +#[cfg(unix)] +pub fn check_root_status() -> Option { + (Uid::effective().is_root()).then_some(FloatingText::new( + ROOT_WARNING.into(), + "Root User Warning", + true, + )) +} diff --git a/tui/src/running_command.rs b/tui/src/running_command.rs index 779a0b3c..c3b3d3d4 100644 --- a/tui/src/running_command.rs +++ b/tui/src/running_command.rs @@ -5,7 +5,7 @@ use portable_pty::{ ChildKiller, CommandBuilder, ExitStatus, MasterPty, NativePtySystem, PtySize, PtySystem, }; use ratatui::{ - crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, + crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind}, layout::{Rect, Size}, style::{Color, Style, Stylize}, text::{Line, Span}, @@ -104,6 +104,18 @@ impl FloatContent for RunningCommand { frame.render_widget(pseudo_term, area); } + fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool { + match event.kind { + MouseEventKind::ScrollUp => { + self.scroll_offset = self.scroll_offset.saturating_add(1); + } + MouseEventKind::ScrollDown => { + self.scroll_offset = self.scroll_offset.saturating_sub(1); + } + _ => {} + } + true + } /// Handle key events of the running command "window". Returns true when the "window" should be /// closed fn handle_key_event(&mut self, key: &KeyEvent) -> bool { diff --git a/tui/src/state.rs b/tui/src/state.rs index 5ee34079..7f96aee9 100644 --- a/tui/src/state.rs +++ b/tui/src/state.rs @@ -4,6 +4,7 @@ use crate::{ float::{Float, FloatContent}, floating_text::FloatingText, hint::{create_shortcut_list, Shortcut}, + root::check_root_status, running_command::RunningCommand, theme::Theme, }; @@ -12,8 +13,8 @@ use linutil_core::{ego_tree::NodeId, Config, ListNode, TabList}; #[cfg(feature = "tips")] use rand::Rng; use ratatui::{ - crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}, - layout::{Alignment, Constraint, Direction, Flex, Layout}, + crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseEvent, MouseEventKind}, + layout::{Alignment, Constraint, Direction, Flex, Layout, Position, Rect}, style::{Style, Stylize}, text::{Line, Span, Text}, widgets::{Block, Borders, List, ListState, Paragraph}, @@ -41,6 +42,8 @@ P* - privileged * "; pub struct AppState { + /// Areas of tabs + areas: Option, /// Selected theme theme: Theme, /// Currently focused area @@ -79,6 +82,11 @@ pub struct ListEntry { pub has_children: bool, } +pub struct Areas { + tab_list: Rect, + list: Rect, +} + enum SelectedItem { UpDir, Directory, @@ -100,6 +108,7 @@ impl AppState { let auto_execute_commands = config_path.map(|path| Config::from_file(&path).auto_execute); let mut state = Self { + areas: None, theme, focus: Focus::List, tabs, @@ -116,6 +125,11 @@ impl AppState { skip_confirmation, }; + #[cfg(unix)] + if let Some(root_warning) = check_root_status() { + state.spawn_float(root_warning, 60, 40); + } + state.update_items(); if let Some(auto_execute_commands) = auto_execute_commands { state.handle_initial_auto_execute(&auto_execute_commands); @@ -315,6 +329,11 @@ impl AppState { .split(horizontal[0]); frame.render_widget(label, left_chunks[0]); + self.areas = Some(Areas { + tab_list: left_chunks[1], + list: horizontal[1], + }); + let tabs = self .tabs .iter() @@ -469,6 +488,59 @@ impl AppState { frame.render_widget(keybind_para, vertical[1]); } + pub fn handle_mouse(&mut self, event: &MouseEvent) -> bool { + if !self.drawable { + return true; + } + + if matches!(self.focus, Focus::TabList | Focus::List) { + let position = Position::new(event.column, event.row); + let mouse_in_tab_list = self.areas.as_ref().unwrap().tab_list.contains(position); + let mouse_in_list = self.areas.as_ref().unwrap().list.contains(position); + + match event.kind { + MouseEventKind::Moved => { + if mouse_in_list { + self.focus = Focus::List + } else if mouse_in_tab_list { + self.focus = Focus::TabList + } + } + MouseEventKind::ScrollDown => { + if mouse_in_tab_list { + if self.current_tab.selected().unwrap() != self.tabs.len() - 1 { + self.current_tab.select_next(); + } + self.refresh_tab(); + } else if mouse_in_list { + self.selection.select_next() + } + } + MouseEventKind::ScrollUp => { + if mouse_in_tab_list { + if self.current_tab.selected().unwrap() != 0 { + self.current_tab.select_previous(); + } + self.refresh_tab(); + } else if mouse_in_list { + self.selection.select_previous() + } + } + _ => {} + } + } + match &mut self.focus { + Focus::FloatingWindow(float) => { + float.content.handle_mouse_event(event); + } + Focus::ConfirmationPrompt(confirm) => { + confirm.content.handle_mouse_event(event); + } + _ => {} + } + true + } + pub fn handle_key(&mut self, key: &KeyEvent) -> bool { // This should be defined first to allow closing // the application even when not drawable ( If terminal is small )