diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 87debf7e..116841e4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,4 +12,6 @@ ignore: - dependency-name: "actions/stale" versions: '>= 9' - dependency-name: "actions/setup-python" - versions: '> 4' \ No newline at end of file + versions: '> 4' + - dependency-name: "crossterm" + versions: '> 0.27.0' diff --git a/Cargo.lock b/Cargo.lock index da9841f1..36fae34a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,9 +163,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", "clap_derive", @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstream", "anstyle", @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index cd718aa5..cacbdcb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] chrono = "0.4.33" -clap = { version = "4.5.11", features = ["derive"] } +clap = { version = "4.5.13", features = ["derive"] } crossterm = "0.27.0" ego-tree = "0.6.2" oneshot = "0.1.8" diff --git a/build/linutil b/build/linutil index 70cac388..abb0de8d 100755 Binary files a/build/linutil and b/build/linutil differ diff --git a/build/linutil-aarch64 b/build/linutil-aarch64 index 64cb6545..78501007 100755 Binary files a/build/linutil-aarch64 and b/build/linutil-aarch64 differ diff --git a/src/commands/applications-setup/dwmtitus-setup.sh b/src/commands/applications-setup/dwmtitus-setup.sh new file mode 100644 index 00000000..e6253ec4 --- /dev/null +++ b/src/commands/applications-setup/dwmtitus-setup.sh @@ -0,0 +1,27 @@ +#!/bin/sh -e +. ./common-script.sh + +makeDWM(){ + cd $HOME && git clone https://github.com/ChrisTitusTech/dwm-titus.git # CD to Home directory to install dwm-titus + # This path can be changed (e.g. to linux-toolbox directory) + cd dwm-titus/ # Hardcoded path, maybe not the best. + sudo ./setup.sh # Run setup + sudo make clean install # Run make clean install +} + +setupDWM() { + echo "Installing DWM-Titus if not already installed" + case "$PACKAGER" in # Install pre-Requisites + pacman) + sudo "$PACKAGER" -S --noconfirm --needed base-devel libx11 libxinerama libxft imlib2 + ;; + *) + sudo "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev + ;; + esac + +} + +checkEnv +setupDWM +makeDWM \ No newline at end of file diff --git a/src/commands/applications-setup/zsh-setup.sh b/src/commands/applications-setup/zsh-setup.sh new file mode 100644 index 00000000..c7a7ea9e --- /dev/null +++ b/src/commands/applications-setup/zsh-setup.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +. ./common-script.sh + +# Function to install zsh +install_zsh() { + echo "Install ZSH if not already installed..." + if ! command_exists zsh; then + case "$PACKAGER" in + pacman) + sudo "$PACKAGER" -Sy --noconfirm zsh + ;; + *) + sudo "$PACKAGER" install -y zsh + ;; + esac + else + echo "ZSH is already installed." + fi +} + +# Function to setup zsh configuration +setup_zsh_config() { + CONFIG_DIR="$HOME/.config/zsh" + ZSHRC_FILE="$CONFIG_DIR/.zshrc" + + if [ ! -d "$CONFIG_DIR" ]; then + mkdir -p "$CONFIG_DIR" + fi + + # Write the configuration to .zshrc + cat <"$ZSHRC_FILE" +HISTFILE=~/.config/zsh/.histfile +HISTSIZE=5000 +SAVEHIST=100000 +setopt autocd extendedglob +unsetopt beep +bindkey -v + +# Configure the prompt with embedded Solarized color codes +PROMPT='%F{32}%n%f%F{166}@%f%F{64}%m:%F{166}%~%f%F{15}$%f ' +RPROMPT='%F{15}(%F{166}%D{%H:%M}%F{15})%f' +EOL + + # Ensure /etc/zsh/zshenv sets ZDOTDIR to the user's config directory + echo 'export ZDOTDIR="$HOME/.config/zsh"' | sudo tee -a /etc/zsh/zshenv +} + +checkEnv +setup_zsh_config +install_zsh \ No newline at end of file diff --git a/src/commands/security/firewall-baselines.sh b/src/commands/security/firewall-baselines.sh index b6153065..0a062289 100644 --- a/src/commands/security/firewall-baselines.sh +++ b/src/commands/security/firewall-baselines.sh @@ -1,11 +1,13 @@ #!/bin/sh -e +. ./common-script.sh + installPkg() { echo "Install UFW if not already installed..." if ! command_exists ufw; then case ${PACKAGER} in pacman) - sudo "${PACKAGER}" -S --noconfirm ufw + sudo "${PACKAGER}" -Sy --noconfirm ufw ;; *) sudo "${PACKAGER}" install -y ufw @@ -14,26 +16,33 @@ installPkg() { else echo "UFW is already installed." fi +} + +configureUFW() { echo -e "${GREEN}Using Chris Titus Recommended Firewall Rules${RC}" - sudo ufw limit 22/tcp + + echo "Disabling UFW" + sudo ufw disable + echo "Limiting port 22/tcp (UFW)" + sudo ufw limit 22/tcp - sudo ufw allow 80/tcp echo "Allowing port 80/tcp (UFW)" + sudo ufw allow 80/tcp - sudo ufw allow 443/tcp echo "Allowing port 443/tcp (UFW)" + sudo ufw allow 443/tcp - sudo ufw default deny incoming echo "Denying Incoming Packets by Default(UFW)" + sudo ufw default deny incoming - sudo ufw default allow outgoing echo "Allowing Outcoming Packets by Default(UFW)" + sudo ufw default allow outgoing sudo ufw enable echo -e "${GREEN}Enabled Firewall with Baselines!${RC}" - } checkEnv installPkg +configureUFW diff --git a/src/commands/system-setup/4-remove-snaps.sh b/src/commands/system-setup/4-remove-snaps.sh new file mode 100644 index 00000000..c839e4f0 --- /dev/null +++ b/src/commands/system-setup/4-remove-snaps.sh @@ -0,0 +1,28 @@ +#!/bin/sh -e + +. ./common-script.sh + +removeSnaps() { + case $PACKAGER in + pacman) + sudo ${PACKAGER} -Rns snapd + ;; + apt-get|nala) + sudo ${PACKAGER} autoremove --purge snapd + if [ "$ID" = ubuntu ]; then + sudo apt-mark hold snapd + fi + ;; + dnf) + sudo ${PACKAGER} remove snapd + ;; + zypper) + sudo ${PACKAGER} remove snapd + ;; + *) + echo "removing snapd not implemented for this package manager" + esac +} + +checkEnv +removeSnaps diff --git a/src/commands/utils/bluetooth-control.sh b/src/commands/utils/bluetooth-control.sh new file mode 100644 index 00000000..ecfd53ad --- /dev/null +++ b/src/commands/utils/bluetooth-control.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +# Function to display colored text +colored_echo() { + local color=$1 + local text=$2 + case $color in + red) echo -e "\033[31m$text\033[0m" ;; + green) echo -e "\033[32m$text\033[0m" ;; + yellow) echo -e "\033[33m$text\033[0m" ;; + blue) echo -e "\033[34m$text\033[0m" ;; + *) echo "$text" ;; + esac +} + +# Function to display the main menu +main_menu() { + while true; do + clear + colored_echo blue "Bluetooth Manager" + colored_echo blue "=================" + echo "1. Scan for devices" + echo "2. Pair with a device" + echo "3. Connect to a device" + echo "4. Disconnect from a device" + echo "5. Remove a device" + echo "0. Exit" + echo -n "Choose an option: " + read -e choice + + case $choice in + 1) scan_devices ;; + 2) pair_device ;; + 3) connect_device ;; + 4) disconnect_device ;; + 5) remove_device ;; + 0) exit 0 ;; + *) colored_echo red "Invalid option. Please try again." ;; + esac + done +} + +# Function to scan for devices +scan_devices() { + clear + colored_echo yellow "Scanning for devices..." + bluetoothctl --timeout 10 scan on + devices=$(bluetoothctl devices) + if [ -z "$devices" ]; then + colored_echo red "No devices found." + else + colored_echo green "Devices found:" + echo "$devices" + fi + echo "Press any key to return to the main menu..." + read -n 1 +} + +# Function to prompt for MAC address using numbers +prompt_for_mac() { + local action=$1 + local command=$2 + local prompt_msg=$3 + local success_msg=$4 + local failure_msg=$5 + + while true; do + clear + devices=$(bluetoothctl devices) + if [ -z "$devices" ]; then + colored_echo red "No devices available. Please scan for devices first." + echo "Press any key to return to the main menu..." + read -n 1 + return + fi + + # Display devices with numbers + IFS=$'\n' read -rd '' -a device_list <<<"$devices" + for i in "${!device_list[@]}"; do + echo "$((i+1)). ${device_list[$i]}" + done + echo "0. Exit to main menu" + echo -n "$prompt_msg" + read -e choice + + # Validate the choice + if [[ $choice =~ ^[0-9]+$ ]] && [ "$choice" -le "${#device_list[@]}" ] && [ "$choice" -gt 0 ]; then + device=${device_list[$((choice-1))]} + mac=$(echo "$device" | awk '{print $2}') + if bluetoothctl info "$mac" > /dev/null 2>&1; then + bluetoothctl $command "$mac" && { + colored_echo green "$success_msg" + break + } || { + colored_echo red "$failure_msg" + } + else + colored_echo red "Invalid MAC address. Please try again." + fi + elif [ "$choice" -eq 0 ]; then + return + else + colored_echo red "Invalid choice. Please try again." + fi + done + echo "Press any key to return to the main menu..." + read -n 1 +} + +# Function to pair with a device +pair_device() { + prompt_for_mac "pair" "pair" "Enter the number of the device to pair: " "Pairing with device completed." "Failed to pair with device." +} + +# Function to connect to a device +connect_device() { + prompt_for_mac "connect" "connect" "Enter the number of the device to connect: " "Connecting to device completed." "Failed to connect to device." +} + +# Function to disconnect from a device +disconnect_device() { + prompt_for_mac "disconnect" "disconnect" "Enter the number of the device to disconnect: " "Disconnecting from device completed." "Failed to disconnect from device." +} + +# Function to remove a device +remove_device() { + prompt_for_mac "remove" "remove" "Enter the number of the device to remove: " "Removing device completed." "Failed to remove device." +} + +# Initialize +main_menu \ No newline at end of file diff --git a/src/commands/utils/monitor-control/auto_detect_displays.sh b/src/commands/utils/monitor-control/auto_detect_displays.sh new file mode 100644 index 00000000..0ff72376 --- /dev/null +++ b/src/commands/utils/monitor-control/auto_detect_displays.sh @@ -0,0 +1,32 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to auto-detect displays and set common resolution +auto_detect_displays() { + if confirm_action "Auto-detect displays and set common resolution?"; then + execute_command "xrandr --auto" + + monitors=$(detect_connected_monitors) + first_monitor=$(echo "$monitors" | head -n 1) + common_resolutions=$(get_unique_resolutions "$first_monitor") + + for monitor in $monitors; do + resolutions=$(get_unique_resolutions "$monitor") + common_resolutions=$(comm -12 <(echo "$common_resolutions") <(echo "$resolutions")) + done + + if [ -z "$common_resolutions" ]; then + dialog --msgbox "No common resolution found among connected monitors." 10 60 + return + fi + + highest_resolution=$(echo "$common_resolutions" | sort -n -t'x' -k1,1 -k2,2 | tail -n 1) + + for monitor in $monitors; do + echo "Setting resolution for $monitor to $highest_resolution" + execute_command "xrandr --output $monitor --mode $highest_resolution" + done + fi +} + +auto_detect_displays diff --git a/src/commands/utils/monitor-control/change_orientation.sh b/src/commands/utils/monitor-control/change_orientation.sh new file mode 100644 index 00000000..46ca1d34 --- /dev/null +++ b/src/commands/utils/monitor-control/change_orientation.sh @@ -0,0 +1,57 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to change monitor orientation +change_orientation() { + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Change Monitor Orientation${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose a monitor to configure:${RESET}" + for i in "${!monitor_array[@]}"; do + echo -e "$((i + 1)). ${CYAN}${monitor_array[i]}${RESET}" + done + + read -p "Enter the number of the monitor: " monitor_choice + + if ! [[ "$monitor_choice" =~ ^[0-9]+$ ]] || (( monitor_choice < 1 )) || (( monitor_choice > ${#monitor_array[@]} )); then + echo -e "${RED}Invalid selection.${RESET}" + return + fi + + monitor_name="${monitor_array[monitor_choice - 1]}" + + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Set Orientation for $monitor_name${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose orientation:${RESET}" + echo -e "1. ${CYAN}Normal${RESET}" + echo -e "2. ${CYAN}Left${RESET}" + echo -e "3. ${CYAN}Right${RESET}" + echo -e "4. ${CYAN}Inverted${RESET}" + + read -p "Enter the number of the orientation: " orientation_choice + + case $orientation_choice in + 1) orientation="normal" ;; + 2) orientation="left" ;; + 3) orientation="right" ;; + 4) orientation="inverted" ;; + *) echo -e "${RED}Invalid selection.${RESET}"; return ;; + esac + + if confirm_action "Change orientation of ${CYAN}$monitor_name${RESET} to ${CYAN}$orientation${RESET}?"; then + echo -e "${GREEN}Changing orientation of $monitor_name to $orientation${RESET}" + execute_command "xrandr --output $monitor_name --rotate $orientation" + echo -e "${GREEN}Orientation changed successfully.${RESET}" + else + echo -e "${RED}Action canceled.${RESET}" + fi +} + +# Call the change_orientation function +change_orientation diff --git a/src/commands/utils/monitor-control/disable_monitor.sh b/src/commands/utils/monitor-control/disable_monitor.sh new file mode 100644 index 00000000..4de80062 --- /dev/null +++ b/src/commands/utils/monitor-control/disable_monitor.sh @@ -0,0 +1,59 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +RESET='\033[0m' +BOLD='\033[1m' +RED='\033[31m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +CYAN='\033[36m' + +# Function to disable a monitor +disable_monitor() { + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Disable Monitor${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose a monitor to disable:${RESET}" + for i in "${!monitor_array[@]}"; do + echo -e "$((i + 1)). ${CYAN}${monitor_array[i]}${RESET}" + done + + read -p "Enter the number of the monitor: " monitor_choice + + if ! [[ "$monitor_choice" =~ ^[0-9]+$ ]] || (( monitor_choice < 1 )) || (( monitor_choice > ${#monitor_array[@]} )); then + echo -e "${RED}Invalid selection.${RESET}" + return + fi + + monitor_name="${monitor_array[monitor_choice - 1]}" + + echo -e "${RED}Warning: Disabling the monitor will turn it off and may affect your display setup.${RESET}" + + if confirm_action "Do you really want to disable ${CYAN}$monitor_name${RESET}?"; then + echo -e "${GREEN}Disabling $monitor_name${RESET}" + execute_command "xrandr --output $monitor_name --off" + echo -e "${GREEN}Monitor $monitor_name disabled successfully.${RESET}" + else + echo -e "${RED}Action canceled.${RESET}" + fi +} + +# Function to prompt for confirmation +confirm_action() { + local action="$1" + echo -e "${BOLD}${YELLOW}$action${RESET}" + read -p "Are you sure? (y/n): " confirm + if [[ "$confirm" =~ ^[Yy]$ ]]; then + return 0 + else + return 1 + fi +} + +# Call the disable_monitor function +disable_monitor diff --git a/src/commands/utils/monitor-control/duplicate_displays.sh b/src/commands/utils/monitor-control/duplicate_displays.sh new file mode 100644 index 00000000..eb6f542d --- /dev/null +++ b/src/commands/utils/monitor-control/duplicate_displays.sh @@ -0,0 +1,15 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to duplicate displays +duplicate_displays() { + primary=$(detect_connected_monitors | head -n 1) + for monitor in $(detect_connected_monitors | tail -n +2); do + if confirm_action "Duplicate $monitor to $primary?"; then + echo "Duplicating $monitor to $primary" + execute_command "xrandr --output $monitor --same-as $primary" + fi + done +} + +duplicate_displays diff --git a/src/commands/utils/monitor-control/enable_monitor.sh b/src/commands/utils/monitor-control/enable_monitor.sh new file mode 100644 index 00000000..b18a7c65 --- /dev/null +++ b/src/commands/utils/monitor-control/enable_monitor.sh @@ -0,0 +1,57 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +RESET='\033[0m' +BOLD='\033[1m' +RED='\033[31m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +CYAN='\033[36m' + +# Function to enable a monitor +enable_monitor() { + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Enable Monitor${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose a monitor to enable:${RESET}" + for i in "${!monitor_array[@]}"; do + echo -e "$((i + 1)). ${CYAN}${monitor_array[i]}${RESET}" + done + + read -p "Enter the number of the monitor: " monitor_choice + + if ! [[ "$monitor_choice" =~ ^[0-9]+$ ]] || (( monitor_choice < 1 )) || (( monitor_choice > ${#monitor_array[@]} )); then + echo -e "${RED}Invalid selection.${RESET}" + return + fi + + monitor_name="${monitor_array[monitor_choice - 1]}" + + if confirm_action "Enable ${CYAN}$monitor_name${RESET}?"; then + echo -e "${GREEN}Enabling $monitor_name${RESET}" + execute_command "xrandr --output $monitor_name --auto" + echo -e "${GREEN}Monitor $monitor_name enabled successfully.${RESET}" + else + echo -e "${RED}Action canceled.${RESET}" + fi +} + +# Function to prompt for confirmation +confirm_action() { + local action="$1" + echo -e "${BOLD}${YELLOW}$action${RESET}" + read -p "Are you sure? (y/n): " confirm + if [[ "$confirm" =~ ^[Yy]$ ]]; then + return 0 + else + return 1 + fi +} + +# Call the enable_monitor function +enable_monitor diff --git a/src/commands/utils/monitor-control/extend_displays.sh b/src/commands/utils/monitor-control/extend_displays.sh new file mode 100644 index 00000000..974b9e82 --- /dev/null +++ b/src/commands/utils/monitor-control/extend_displays.sh @@ -0,0 +1,15 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to extend displays +extend_displays() { + monitors=($(detect_connected_monitors)) + for ((i=1; i<${#monitors[@]}; i++)); do + if confirm_action "Extend ${monitors[$i]} to the right of ${monitors[$((i-1))]}?"; then + echo "Extending ${monitors[$i]} to the right of ${monitors[$((i-1))]}" + execute_command "xrandr --output ${monitors[$i]} --right-of ${monitors[$((i-1))]}" + fi + done +} + +extend_displays diff --git a/src/commands/utils/monitor-control/manage_arrangement.sh b/src/commands/utils/monitor-control/manage_arrangement.sh new file mode 100644 index 00000000..7637641d --- /dev/null +++ b/src/commands/utils/monitor-control/manage_arrangement.sh @@ -0,0 +1,70 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to manage monitor arrangement +manage_arrangement() { + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Manage Monitor Arrangement${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose the monitor to arrange:${RESET}" + for i in "${!monitor_array[@]}"; do + echo -e "$((i + 1)). ${CYAN}${monitor_array[i]}${RESET}" + done + + read -p "Enter the number of the monitor to arrange: " monitor_choice + + if ! [[ "$monitor_choice" =~ ^[0-9]+$ ]] || (( monitor_choice < 1 )) || (( monitor_choice > ${#monitor_array[@]} )); then + echo -e "${RED}Invalid selection.${RESET}" + return + fi + + monitor_name="${monitor_array[monitor_choice - 1]}" + + clear + echo -e "${YELLOW}Choose position relative to other monitors:${RESET}" + echo -e "1. ${CYAN}Left of${RESET}" + echo -e "2. ${CYAN}Right of${RESET}" + echo -e "3. ${CYAN}Above${RESET}" + echo -e "4. ${CYAN}Below${RESET}" + + read -p "Enter the number of the position: " position_choice + + case $position_choice in + 1) position="--left-of" ;; + 2) position="--right-of" ;; + 3) position="--above" ;; + 4) position="--below" ;; + *) echo -e "${RED}Invalid selection.${RESET}"; return ;; + esac + + echo -e "${YELLOW}Choose the reference monitor:${RESET}" + for i in "${!monitor_array[@]}"; do + if [[ "${monitor_array[i]}" != "$monitor_name" ]]; then + echo -e "$((i + 1)). ${CYAN}${monitor_array[i]}${RESET}" + fi + done + + read -p "Enter the number of the reference monitor: " ref_choice + + if ! [[ "$ref_choice" =~ ^[0-9]+$ ]] || (( ref_choice < 1 )) || (( ref_choice > ${#monitor_array[@]} )) || (( ref_choice == monitor_choice )); then + echo -e "${RED}Invalid selection.${RESET}" + return + fi + + ref_monitor="${monitor_array[ref_choice - 1]}" + + if confirm_action "Arrange ${CYAN}$monitor_name${RESET} ${position} ${CYAN}$ref_monitor${RESET}?"; then + echo -e "${GREEN}Arranging $monitor_name ${position} $ref_monitor${RESET}" + execute_command "xrandr --output $monitor_name $position $ref_monitor" + echo -e "${GREEN}Arrangement updated successfully.${RESET}" + else + echo -e "${RED}Action canceled.${RESET}" + fi +} + +# Call the manage_arrangement function +manage_arrangement diff --git a/src/commands/utils/monitor-control/reset_scaling.sh b/src/commands/utils/monitor-control/reset_scaling.sh new file mode 100644 index 00000000..73f03ca8 --- /dev/null +++ b/src/commands/utils/monitor-control/reset_scaling.sh @@ -0,0 +1,22 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to reset scaling back to 1 (native resolution) for all monitors +reset_scaling() { + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Reset Monitor Scaling to Native Resolution${RESET}" + echo -e "${BLUE}=========================================${RESET}" + + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + for monitor in "${monitor_array[@]}"; do + echo -e "${CYAN}Resetting scaling for $monitor to 1x1 (native resolution)${RESET}" + execute_command "xrandr --output $monitor --scale 1x1" + done + + echo -e "${GREEN}All monitor scalings have been reset to 1x1.${RESET}" +} + +# Call the reset_scaling function +reset_scaling diff --git a/src/commands/utils/monitor-control/scale_monitor.sh b/src/commands/utils/monitor-control/scale_monitor.sh new file mode 100644 index 00000000..4c88738b --- /dev/null +++ b/src/commands/utils/monitor-control/scale_monitor.sh @@ -0,0 +1,43 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to scale smaller monitors to the highest resolution of a bigger monitor +scale_monitors() { + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Scale Monitors to Highest Resolution${RESET}" + echo -e "${BLUE}=========================================${RESET}" + + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + # Get the highest resolution among all monitors + max_width=0 + max_height=0 + + for monitor in "${monitor_array[@]}"; do + res=$(xrandr | grep -A1 "^$monitor connected" | tail -1 | awk '{print $1}') + width=$(echo $res | awk -Fx '{print $1}') + height=$(echo $res | awk -Fx '{print $2}') + + if (( width > max_width )); then + max_width=$width + fi + + if (( height > max_height )); then + max_height=$height + fi + done + + echo -e "${CYAN}Highest resolution found: ${max_width}x${max_height}${RESET}" + + # Scale all monitors to the maximum resolution + for monitor in "${monitor_array[@]}"; do + echo -e "${CYAN}Scaling $monitor to ${max_width}x${max_height}${RESET}" + execute_command "xrandr --output $monitor --scale-from ${max_width}x${max_height}" + done + + echo -e "${GREEN}Scaling complete. All monitors are now scaled to ${max_width}x${max_height}.${RESET}" +} + +# Call the scale_monitors function +scale_monitors diff --git a/src/commands/utils/monitor-control/set_primary_monitor.sh b/src/commands/utils/monitor-control/set_primary_monitor.sh new file mode 100644 index 00000000..68d8a17f --- /dev/null +++ b/src/commands/utils/monitor-control/set_primary_monitor.sh @@ -0,0 +1,37 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +# Function to set a monitor as primary +set_primary_monitor() { + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Set Primary Monitor${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose a monitor to set as primary:${RESET}" + for i in "${!monitor_array[@]}"; do + echo -e "$((i + 1)). ${CYAN}${monitor_array[i]}${RESET}" + done + + read -p "Enter the number of the monitor: " monitor_choice + + if ! [[ "$monitor_choice" =~ ^[0-9]+$ ]] || (( monitor_choice < 1 )) || (( monitor_choice > ${#monitor_array[@]} )); then + echo -e "${RED}Invalid selection.${RESET}" + return + fi + + monitor_name="${monitor_array[monitor_choice - 1]}" + + if confirm_action "Set ${CYAN}$monitor_name${RESET} as the primary monitor?"; then + echo -e "${GREEN}Setting $monitor_name as primary monitor${RESET}" + execute_command "xrandr --output $monitor_name --primary" + echo -e "${GREEN}Monitor $monitor_name set as primary successfully.${RESET}" + else + echo -e "${RED}Action canceled.${RESET}" + fi +} + +# Call the set_primary_monitor function +set_primary_monitor diff --git a/src/commands/utils/monitor-control/set_resolutions.sh b/src/commands/utils/monitor-control/set_resolutions.sh new file mode 100644 index 00000000..40eab9f6 --- /dev/null +++ b/src/commands/utils/monitor-control/set_resolutions.sh @@ -0,0 +1,92 @@ +#!/bin/bash +source ./utils/monitor-control/utility_functions.sh + +RESET='\033[0m' +BOLD='\033[1m' +RED='\033[31m' +GREEN='\033[32m' +YELLOW='\033[33m' +BLUE='\033[34m' +CYAN='\033[36m' + +# Function to set resolutions +set_resolutions() { + monitor_list=$(detect_connected_monitors) + IFS=$'\n' read -r -d '' -a monitor_array <<<"$monitor_list" + + while true; do + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Monitor Configuration${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose a monitor to configure:${RESET}" + for i in "${!monitor_array[@]}"; do + echo -e "$((i + 1)). ${CYAN}${monitor_array[i]}${RESET}" + done + + read -p "Enter the choice (or 'q' to quit): " monitor_choice + + if [[ "$monitor_choice" == "q" ]]; then + echo -e "${RED}Exiting...${RESET}" + return + fi + + if ! [[ "$monitor_choice" =~ ^[0-9]+$ ]] || (( monitor_choice < 1 )) || (( monitor_choice > ${#monitor_array[@]} )); then + echo -e "${RED}Invalid selection. Please try again.${RESET}" + read -p "Press [Enter] to continue..." + continue + fi + + monitor_name="${monitor_array[monitor_choice - 1]}" + resolutions=$(get_unique_resolutions "$monitor_name") + + # Create a temporary file with sorted resolutions and indices + temp_res_file=$(mktemp) + echo "$resolutions" | sort -nr | awk '{print NR " " $0}' > "$temp_res_file" + + # Read the sorted resolutions into an associative array + declare -A resolution_map + while read -r index resolution; do + resolution_map[$index]="$resolution" + done < "$temp_res_file" + + clear + echo -e "${BLUE}=========================================${RESET}" + echo -e "${BLUE} Resolution Configuration for ${CYAN}$monitor_name${RESET}" + echo -e "${BLUE}=========================================${RESET}" + echo -e "${YELLOW}Choose resolution for $monitor_name:${RESET}" + awk '{print $1 ". " $2}' "$temp_res_file" + + while true; do + read -p "Enter the choice (or 'q' to quit): " resolution_choice + + if [[ "$resolution_choice" == "q" ]]; then + echo -e "${RED}Exiting...${RESET}" + rm "$temp_res_file" + return + fi + + if ! [[ "$resolution_choice" =~ ^[0-9]+$ ]] || (( resolution_choice < 1 )) || (( resolution_choice > ${#resolution_map[@]} )); then + echo -e "${RED}Invalid selection. Please try again.${RESET}" + continue + fi + + # Map the index to the actual resolution + selected_resolution=${resolution_map[$resolution_choice]} + + read -p "Set resolution for $monitor_name to $selected_resolution? (y/n): " confirm + if [[ "$confirm" =~ ^[Yy]$ ]]; then + echo -e "${GREEN}Setting resolution for $monitor_name to $selected_resolution${RESET}" + execute_command "xrandr --output $monitor_name --mode $selected_resolution" + break + else + echo -e "${RED}Action canceled. Please choose a different resolution.${RESET}" + fi + done + + # Clean up the temporary file + rm "$temp_res_file" + done +} + +set_resolutions diff --git a/src/commands/utils/monitor-control/utility_functions.sh b/src/commands/utils/monitor-control/utility_functions.sh new file mode 100644 index 00000000..9a467bfd --- /dev/null +++ b/src/commands/utils/monitor-control/utility_functions.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Function to execute xrandr commands and handle errors +execute_command() { + local command="$1" + echo "Executing: $command" + eval "$command" 2>&1 | tee /tmp/xrandr.log | tail -n 20 + if [ $? -ne 0 ]; then + echo "An error occurred while executing the command. Check /tmp/xrandr.log for details." + fi +} + +# Function to detect connected monitors +detect_connected_monitors() { + xrandr_output=$(xrandr) + echo "$xrandr_output" | grep " connected" | awk '{print $1}' +} + +# Function to get resolutions for a monitor +get_unique_resolutions() { + local monitor="$1" + xrandr_output=$(xrandr) + echo "$xrandr_output" | grep -A 10 "$monitor connected" | grep -oP '\d+x\d+' | sort -u +} + +# Function to prompt for confirmation +confirm_action() { + local action="$1" + echo "$action" + read -p "Are you sure? (y/n): " confirm + if [[ "$confirm" =~ ^[Yy]$ ]]; then + return 0 + else + return 1 + fi +} diff --git a/src/commands/utils/wifi-control.sh b/src/commands/utils/wifi-control.sh new file mode 100644 index 00000000..884ab360 --- /dev/null +++ b/src/commands/utils/wifi-control.sh @@ -0,0 +1,169 @@ +@@ -0,0 +1,168 @@ +#!/bin/bash + +# Function to display colored text +colored_echo() { + local color=$1 + local text=$2 + case $color in + red) echo -e "\033[31m$text\033[0m" ;; + green) echo -e "\033[32m$text\033[0m" ;; + yellow) echo -e "\033[33m$text\033[0m" ;; + blue) echo -e "\033[34m$text\033[0m" ;; + *) echo "$text" ;; + esac +} + +# Function to display the main menu +main_menu() { + while true; do + clear + colored_echo blue "WiFi Manager" + colored_echo blue "============" + echo "1. Turn WiFi On" + echo "2. Turn WiFi Off" + echo "3. Scan for WiFi networks" + echo "4. Connect to a WiFi network" + echo "5. Disconnect from a WiFi network" + echo "6. Remove a WiFi connection" + echo "0. Exit" + echo -n "Choose an option: " + read -e choice + + case $choice in + 1) wifi_on ;; + 2) wifi_off ;; + 3) scan_networks ;; + 4) connect_network ;; + 5) disconnect_network ;; + 6) remove_network ;; + 0) exit 0 ;; + *) colored_echo red "Invalid option. Please try again." ;; + esac + done +} + +# Function to scan for WiFi networks +scan_networks() { + clear + colored_echo yellow "Scanning for WiFi networks..." + networks=$(nmcli -t -f SSID,BSSID,SIGNAL dev wifi list | head -n 10) + if [ -z "$networks" ]; then + colored_echo red "No networks found." + else + colored_echo green "Top 10 Networks found:" + echo "$networks" | sed 's/\\//g' | awk -F: '{printf("%d. SSID: %-25s \n", NR, $1)}' + fi + echo "Press any key to return to the main menu..." + read -n 1 +} + +# Function to turn WiFi on +wifi_on() { + clear + colored_echo yellow "Turning WiFi on..." + nmcli radio wifi on && { + colored_echo green "WiFi is now turned on." + } || { + colored_echo red "Failed to turn on WiFi." + } + echo "Press any key to return to the main menu..." + read -n 1 +} + +# Function to turn WiFi off +wifi_off() { + clear + colored_echo yellow "Turning WiFi off..." + nmcli radio wifi off && { + colored_echo green "WiFi is now turned off." + } || { + colored_echo red "Failed to turn off WiFi." + } + echo "Press any key to return to the main menu..." + read -n 1 +} + +# Function to prompt for WiFi network selection +prompt_for_network() { + local action=$1 + local prompt_msg=$2 + local success_msg=$3 + local failure_msg=$4 + + while true; do + clear + networks=$(nmcli -t -f SSID dev wifi list | head -n 10) + if [ -z "$networks" ]; then + colored_echo red "No networks available. Please scan for networks first." + echo "Press any key to return to the main menu..." + read -n 1 + return + fi + + # Display networks with numbers + IFS=$'\n' read -rd '' -a network_list <<<"$networks" + for i in "${!network_list[@]}"; do + ssid=$(echo "${network_list[$i]}" | awk -F: '{print $1}') + echo "$((i+1)). SSID: $ssid" + done + echo "0. Exit to main menu" + echo -n "$prompt_msg" + read -e choice + + # Validate the choice + if [[ $choice =~ ^[0-9]+$ ]] && [ "$choice" -le "${#network_list[@]}" ] && [ "$choice" -gt 0 ]; then + network=${network_list[$((choice-1))]} + ssid=$(echo "$network" | awk -F: '{print $1}') + if [ "$action" == "connect" ]; then + echo -n "Enter password for SSID $ssid: " + read -s password + echo + nmcli dev wifi connect "$ssid" password "$password" && { + colored_echo green "$success_msg" + break + } || { + colored_echo red "$failure_msg" + } + elif [ "$action" == "disconnect" ]; then + nmcli connection down "$ssid" && { + colored_echo green "$success_msg" + break + } || { + colored_echo red "$failure_msg" + } + elif [ "$action" == "remove" ]; then + nmcli connection delete "$ssid" && { + colored_echo green "$success_msg" + break + } || { + colored_echo red "$failure_msg" + } + fi + elif [ "$choice" -eq 0 ]; then + return + else + colored_echo red "Invalid choice. Please try again." + fi + done + echo "Press any key to return to the main menu..." + read -n 1 +} + +# Function to connect to a WiFi network +connect_network() { + prompt_for_network "connect" "Enter the number of the network to connect: " "Connected to the network successfully." "Failed to connect to the network." +} + +# Function to disconnect from a WiFi network +disconnect_network() { + prompt_for_network "disconnect" "Enter the number of the network to disconnect: " "Disconnected from the network successfully." "Failed to disconnect from the network." +} + +# Function to remove a WiFi connection +remove_network() { + prompt_for_network "remove" "Enter the number of the network to remove: " "Network removed successfully." "Failed to remove the network." +} + +# Initialize +main_menu \ No newline at end of file diff --git a/src/list.rs b/src/list.rs index 90a4f265..d50ed715 100644 --- a/src/list.rs +++ b/src/list.rs @@ -58,6 +58,49 @@ impl CustomList { name: "root", command: Command::None, } => { + ListNode { + name: "Applications Setup", + command: Command::None + } => { + ListNode { + name: "Alacritty", + command: Command::LocalFile("applications-setup/alacritty-setup.sh"), + }, + ListNode { + name: "Bash Prompt", + command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""), + }, + ListNode { + name: "DWM-Titus", + command: Command::LocalFile("applications-setup/dwmtitus-setup.sh") + }, + ListNode { + name: "Kitty", + command: Command::LocalFile("applications-setup/kitty-setup.sh") + }, + ListNode { + name: "Neovim", + command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""), + }, + ListNode { + name: "Rofi", + command: Command::LocalFile("applications-setup/rofi-setup.sh"), + }, + ListNode { + name: "ZSH Prompt", + command: Command::LocalFile("applications-setup/zsh-setup.sh"), + } + + }, + ListNode { + name: "Security", + command: Command::None + } => { + ListNode { + name: "Firewall Baselines (CTT)", + command: Command::LocalFile("security/firewall-baselines.sh"), + } + }, ListNode { name: "System Setup", command: Command::None, @@ -74,39 +117,71 @@ impl CustomList { name: "Global Theme", command: Command::LocalFile("system-setup/3-global-theme.sh"), }, + ListNode { + name: "Remove Snaps", + command: Command::LocalFile("system-setup/4-remove-snaps.sh"), + }, }, ListNode { - name: "Security", + name: "Utilities", command: Command::None } => { ListNode { - name: "Firewall Baselines (CTT)", - command: Command::LocalFile("security/firewall-baselines.sh"), - } - }, - ListNode { - name: "Applications Setup", - command: Command::None - } => { - ListNode { - name: "Alacritty Setup", - command: Command::LocalFile("applications-setup/alacritty-setup.sh"), + name: "Wifi Manager", + command: Command::LocalFile("utils/wifi-control.sh"), }, ListNode { - name: "Bash Prompt Setup", - command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/mybash/main/setup.sh)\""), + name: "Bluetooth Manager", + command: Command::LocalFile("utils/bluetooth-control.sh"), }, ListNode { - name: "Kitty Setup", - command: Command::LocalFile("applications-setup/kitty-setup.sh") - }, - ListNode { - name: "Neovim Setup", - command: Command::Raw("bash -c \"$(curl -s https://raw.githubusercontent.com/ChrisTitusTech/neovim/main/setup.sh)\""), - }, - ListNode { - name: "Rofi Setup", - command: Command::LocalFile("applications-setup/rofi-setup.sh"), + name: "MonitorControl(xorg)", + command: Command::None, + } => { + ListNode { + name: "Set Resolution", + command: Command::LocalFile("utils/monitor-control/set_resolutions.sh"), + }, + ListNode { + name: "Duplicate Displays", + command: Command::LocalFile("utils/monitor-control/duplicate_displays.sh"), + }, + ListNode { + name: "Extend Displays", + command: Command::LocalFile("utils/monitor-control/extend_displays.sh"), + }, + ListNode { + name: "Auto Detect Displays", + command: Command::LocalFile("utils/monitor-control/auto_detect_displays.sh"), + }, + ListNode { + name: "Enable Monitor", + command: Command::LocalFile("utils/monitor-control/enable_monitor.sh"), + }, + ListNode { + name: "Disable Monitor", + command: Command::LocalFile("utils/monitor-control/disable_monitor.sh"), + }, + ListNode { + name: "Set Primary Monitor", + command: Command::LocalFile("utils/monitor-control/set_primary_monitor.sh"), + }, + ListNode { + name: "Change Orientation", + command: Command::LocalFile("utils/monitor-control/change_orientation.sh"), + }, + ListNode { + name: "Manage Arrangement", + command: Command::LocalFile("utils/monitor-control/manage_arrangement.sh"), + }, + ListNode { + name: "Scale Monitors", + command: Command::LocalFile("utils/monitor-control/scale_monitor.sh"), + }, + ListNode { + name: "Reset Scaling", + command: Command::LocalFile("utils/monitor-control/reset_scaling.sh"), + }, }, }, ListNode { @@ -259,10 +334,10 @@ impl CustomList { // so the scroll does not happen in the main window as well if self.preview_window_state.is_some() { self.scroll_preview_window_down(); - return None; + } else { + self.list_state.select_next(); } - self.list_state.select_next(); None } KeyCode::Char('k') | KeyCode::Up => { @@ -270,10 +345,10 @@ impl CustomList { // so the scroll does not happen in the main window as well if self.preview_window_state.is_some() { self.scroll_preview_window_up(); - return None; + } else { + self.list_state.select_previous(); } - self.list_state.select_previous(); None } // The 'p' key toggles the preview on and off diff --git a/src/main.rs b/src/main.rs index 3d82461c..f00382fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,15 +142,6 @@ fn run(terminal: &mut Terminal, state: &AppState) -> io::Result<( command_opt = None; } } else { - if key.code == KeyCode::Char('q') { - return Ok(()); - } - //Activate search mode if the forward slash key gets pressed - if key.code == KeyCode::Char('/') { - // Enter search mode - in_search_mode = true; - continue; - } //Insert user input into the search bar if in_search_mode { match key.code { @@ -175,6 +166,18 @@ fn run(terminal: &mut Terminal, state: &AppState) -> io::Result<( } } else if let Some(cmd) = custom_list.handle_key(key, state) { command_opt = Some(RunningCommand::new(cmd, state)); + } else { + // Handle keys while not in search mode + match key.code { + // Exit the program + KeyCode::Char('q') => return Ok(()), + //Activate search mode if the forward slash key gets pressed + KeyCode::Char('/') => { + in_search_mode = true; + continue; + } + _ => {} + } } } }