31 changed files with 45 additions and 1823 deletions
			
			
		@ -1,98 +1,20 @@ | 
				
			|||
#!/bin/env bash | 
				
			|||
 | 
				
			|||
source ./src/scripts/utils/source.sh | 
				
			|||
source ./src/scripts/utils/progressbar.sh | 
				
			|||
 | 
				
			|||
while getopts 'v' OPTION; do | 
				
			|||
    case "$OPTION" in | 
				
			|||
        v) | 
				
			|||
          _V=1 | 
				
			|||
          ;; | 
				
			|||
        ?) | 
				
			|||
          echo "usage: ./setup.sh [-v]" >&2 | 
				
			|||
          exit 1 | 
				
			|||
          ;; | 
				
			|||
    esac | 
				
			|||
done | 
				
			|||
 | 
				
			|||
enable_trapping | 
				
			|||
draw_progress_bar 0 | 
				
			|||
log '.: Setting up sono-os v0.1.0 :.' | 
				
			|||
check_superuser | 
				
			|||
sleep 1 | 
				
			|||
draw_progress_bar 5 | 
				
			|||
log 'Installing dependancies ...' | 
				
			|||
sudo apt -qq update | 
				
			|||
draw_progress_bar 10 | 
				
			|||
sudo apt -qq install -y python3-pip build-essential | 
				
			|||
draw_progress_bar 15 | 
				
			|||
 | 
				
			|||
log 'Installing pyxlib and pyudev ...' | 
				
			|||
pip3 install pyxlib pyudev -q | 
				
			|||
draw_progress_bar 20 | 
				
			|||
 | 
				
			|||
log 'Installing X scripts ...' | 
				
			|||
sudo cp -r src/scripts/xscript/* /usr/local/bin | 
				
			|||
draw_progress_bar 23 | 
				
			|||
sudo cp src/scripts/setupmonitor.sh /usr/local/bin | 
				
			|||
draw_progress_bar 27 | 
				
			|||
sudo chmod +x /usr/local/bin/setupmonitor.sh | 
				
			|||
draw_progress_bar 30 | 
				
			|||
sudo chmod +x /usr/local/bin/changemouse.py | 
				
			|||
 | 
				
			|||
log 'Installing Sleep scripts ...' | 
				
			|||
sudo cp src/scripts/utils/socknix.py /usr/local/bin | 
				
			|||
draw_progress_bar 33 | 
				
			|||
sudo chmod +x /usr/local/bin/socknix.py | 
				
			|||
draw_progress_bar 35 | 
				
			|||
sudo cp src/scripts/sleeputil.sh /lib/systemd/system-sleep/ | 
				
			|||
draw_progress_bar 36 | 
				
			|||
sudo chmod +x /lib/systemd/system-sleep/sleeputil.sh | 
				
			|||
 | 
				
			|||
 | 
				
			|||
draw_progress_bar 45 | 
				
			|||
log 'Installing config files ...' | 
				
			|||
# TODO | 
				
			|||
 | 
				
			|||
draw_progress_bar 65 | 
				
			|||
log 'Configuring Logger ...' | 
				
			|||
# TODO | 
				
			|||
 | 
				
			|||
draw_progress_bar 85 | 
				
			|||
log 'Copying rules to udev ...' | 
				
			|||
sudo cp -r src/rules/* /usr/lib/udev/rules.d | 
				
			|||
sudo cp src/rules/90-usb-storage.rules /etc/udev/rules.d | 
				
			|||
sudo cp src/rules/90-dvd-storage.rules /etc/udev/rules.d | 
				
			|||
sudo cp -r src/rules/* /etc/udev/rules.d | 
				
			|||
sudo udevadm control --reload-rules  | 
				
			|||
sudo udevadm trigger | 
				
			|||
 | 
				
			|||
draw_progress_bar 90 | 
				
			|||
sudo cp src/scripts/utils/usb/usb-storage-action /usr/local/bin/ | 
				
			|||
sudo chmod +x /usr/local/bin/usb-storage-action | 
				
			|||
sudo cp src/scripts/utils/usb/addUsbScript.sh /usr/local/bin/ | 
				
			|||
sudo chmod +x /usr/local/bin/addUsbScript.sh | 
				
			|||
sudo cp src/scripts/utils/usb/usb-storage-action.sh /usr/local/bin/ | 
				
			|||
sudo chmod +x /usr/local/bin/usb-storage-action.sh | 
				
			|||
sudo cp src/scripts/utils/usb/usb-socket-sono.socket /etc/systemd/system | 
				
			|||
sudo cp src/scripts/utils/usb/usb-socket-sono.service /etc/systemd/system | 
				
			|||
sudo systemctl enable usb-socket-sono.socket | 
				
			|||
sudo systemctl enable usb-socket-sono.service | 
				
			|||
sudo systemctl start usb-socket-sono.socket | 
				
			|||
sudo systemctl start usb-socket-sono.service | 
				
			|||
 | 
				
			|||
sudo cp src/scripts/utils/dvd/dvd-storage-action /usr/local/bin/ | 
				
			|||
sudo chmod +x /usr/local/bin/dvd-storage-action | 
				
			|||
sudo cp src/scripts/utils/dvd/addDvdScript.sh /usr/local/bin/ | 
				
			|||
sudo chmod +x /usr/local/bin/addDvdScript.sh | 
				
			|||
sudo cp src/scripts/utils/dvd/dvd-storage-action.sh /usr/local/bin/ | 
				
			|||
sudo chmod +x /usr/local/bin/dvd-storage-action.sh | 
				
			|||
sudo cp src/scripts/utils/dvd/dvd-socket-sono.socket /etc/systemd/system | 
				
			|||
sudo cp src/scripts/utils/dvd/dvd-socket-sono.service /etc/systemd/system | 
				
			|||
sudo systemctl enable dvd-socket-sono.socket | 
				
			|||
sudo systemctl enable dvd-socket-sono.service | 
				
			|||
sudo systemctl start dvd-socket-sono.socket | 
				
			|||
sudo systemctl start dvd-socket-sono.service | 
				
			|||
 | 
				
			|||
 | 
				
			|||
draw_progress_bar 95 | 
				
			|||
log 'Install PCIE Driver' | 
				
			|||
# TODO | 
				
			|||
 | 
				
			|||
draw_progress_bar 100 | 
				
			|||
destroy_scroll_area | 
				
			|||
@ -1 +0,0 @@ | 
				
			|||
SUBSYSTEM=="drm", ENV{MONITOR_LOCK}="/tmp/monitorlock", ENV{SONOLOG}="/tmp/sonolog.log", RUN+="/usr/local/bin/setupmonitor.sh" | 
				
			|||
@ -1,5 +1,5 @@ | 
				
			|||
# Rule for DVD disk insertion | 
				
			|||
#ACTION=="change", SUBSYSTEM=="block", KERNEL=="sr[0-9]*", ENV{ID_CDROM_MEDIA}=="1", RUN+="/usr/bin/sudo /usr/local/bin/dvd-storage-action add %E{DEVNAME}" | 
				
			|||
#ACTION=="change", SUBSYSTEM=="block", KERNEL=="sr[0-9]*", ENV{ID_CDROM_MEDIA}=="1", RUN+="/usr/bin/sudo /usr/local/bin/dvd-storage-action.sh add %E{DEVNAME}" | 
				
			|||
# Rule for DVD disk removal | 
				
			|||
#ACTION=="change", SUBSYSTEM=="block", KERNEL=="sr[0-9]*", ENV{ID_CDROM_MEDIA}!="1", RUN+="/usr/bin/sudo /usr/local/bin/dvd-storage-action remove %E{DEVNAME}" | 
				
			|||
#ACTION=="change", SUBSYSTEM=="block", KERNEL=="sr[0-9]*", ENV{ID_CDROM_MEDIA}!="1", RUN+="/usr/bin/sudo /usr/local/bin/dvd-storage-action.sh remove %E{DEVNAME}" | 
				
			|||
ACTION=="change", SUBSYSTEM=="block", KERNEL=="sr[0-9]*", ENV{ID_CDROM}=="1", RUN+="/usr/bin/sudo /usr/local/bin/dvd-storage-action %E{ACTION} %E{DEVNAME}" | 
				
			|||
 | 
				
			|||
@ -1,13 +0,0 @@ | 
				
			|||
# Following rule will take affect after binding/unbinding action of HID devices | 
				
			|||
# which is expected. These rules only take effect one time for each mouse | 
				
			|||
# insertion, removal. Actually, usb events are triggered when usb and | 
				
			|||
# usb interface are binding but hid is more specific and happen once for each | 
				
			|||
# action. change action is not used because  | 
				
			|||
# XAUTHORUTY and DISPLAY must be set and related to the current session. It may | 
				
			|||
# vary with different graphic drivers. | 
				
			|||
SUBSYSTEM=="hid", ENV{DISPLAY}=":0", ENV{XAUTHORITY}="/run/user/1000/gdm/Xauthority", ACTION=="bind", RUN+="/usr/local/bin/changemouse.py" | 
				
			|||
 | 
				
			|||
# the following line may not be needed as when we unplug a mouse it would | 
				
			|||
# automatically remove devices from the list and they need no furthur | 
				
			|||
# configuration | 
				
			|||
# SUBSYSTEM=="hid", ACTION=="unbind", RUN+="/usr/local/bin/changemouse.py" | 
				
			|||
@ -1,4 +1,4 @@ | 
				
			|||
# Rule for USB device connection | 
				
			|||
ACTION=="add", SUBSYSTEM=="block", ENV{ID_BUS}=="usb", ENV{ID_FS_USAGE}=="filesystem", RUN+="/usr/bin/sudo /usr/local/bin/usb-storage-action add %E{DEVNAME}" | 
				
			|||
ACTION=="add", SUBSYSTEM=="block", ENV{ID_BUS}=="usb", ENV{ID_FS_USAGE}=="filesystem", RUN+="/usr/bin/sudo /usr/local/bin/usb-storage-action.sh add %E{DEVNAME}" | 
				
			|||
# Rule for USB device disconnection | 
				
			|||
ACTION=="remove", SUBSYSTEM=="block", ENV{ID_BUS}=="usb", ENV{ID_FS_USAGE}=="filesystem", RUN+="/usr/bin/sudo /usr/local/bin/usb-storage-action remove %E{DEVNAME}" | 
				
			|||
ACTION=="remove", SUBSYSTEM=="block", ENV{ID_BUS}=="usb", ENV{ID_FS_USAGE}=="filesystem", RUN+="/usr/bin/sudo /usr/local/bin/usb-storage-action.sh remove %E{DEVNAME}" | 
				
			|||
 | 
				
			|||
@ -1,39 +0,0 @@ | 
				
			|||
# /bin/sh | 
				
			|||
# These things are run when an Openbox X Session is started. | 
				
			|||
# You may place a similar script in $HOME/.config/openbox/autostart | 
				
			|||
# to run user-specific things. | 
				
			|||
if true; then | 
				
			|||
MAX_CRASH=5000 | 
				
			|||
CRASH_COUNT=0 | 
				
			|||
while true; do | 
				
			|||
    sudo /home/hasis/Desktop/build-sono-Desktop_Qt_5_13_1_GCC_64bit-Debug/ui/sono & | 
				
			|||
    PID=$! | 
				
			|||
 | 
				
			|||
    wait $PID | 
				
			|||
 | 
				
			|||
    if [ "$(expr 1 = 0)" -eq 1 ]; then | 
				
			|||
        CRASH_COUNT=0 | 
				
			|||
        exit 0 | 
				
			|||
    else | 
				
			|||
        CRASH_COUNT="$(expr $CRASH_COUNT + 1)" | 
				
			|||
        if [ $(expr $CRASH_COUNT ">=" $MAX_CRASH) -eq 1 ]; then | 
				
			|||
            echo "[$(date)] Program has crashed $CRASH_COUNT times, giving up."  >> /var/log/crashreport.log | 
				
			|||
            exit 1 | 
				
			|||
        else | 
				
			|||
            echo "[$(date)] Program has crashed $CRASH_COUNT times, restarting." >> /var/log/crashreport.log | 
				
			|||
        fi | 
				
			|||
    fi | 
				
			|||
done | 
				
			|||
fi & | 
				
			|||
 | 
				
			|||
# If you want to use GNOME config tools... | 
				
			|||
# | 
				
			|||
#if test -x /usr/lib/x86_64-linux-gnu/gnome-settings-daemon >/dev/null; then | 
				
			|||
#  /usr/lib/x86_64-linux-gnu/gnome-settings-daemon & | 
				
			|||
#elif which gnome-settings-daemon >/dev/null 2>&1; then | 
				
			|||
#  gnome-settings-daemon & | 
				
			|||
#fi | 
				
			|||
 | 
				
			|||
# If you want to use XFCE config tools... | 
				
			|||
# | 
				
			|||
#xfce-mcs-manager & | 
				
			|||
@ -1,11 +0,0 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
# This script will run when drm change event detected. | 
				
			|||
# This sctipt should be placed in /usr/local/bin | 
				
			|||
# SONOLOG file must be set beforehand in the udev rule | 
				
			|||
# MONITOR_LOCK should be set | 
				
			|||
( | 
				
			|||
    flock -n 100 || exit 1 | 
				
			|||
    sleep 1 # wait until all changes take place | 
				
			|||
    python3 /usr/bin/local/setupmonitor.py | 
				
			|||
    echo $(data) - INFO - Setup Monitor Done >> $SONOLOG | 
				
			|||
) 100> $MONITOR_LOCK | 
				
			|||
@ -1,21 +0,0 @@ | 
				
			|||
#!/bin/sh | 
				
			|||
 | 
				
			|||
# should be moved to the /lib/systemd/system-sleep/ | 
				
			|||
 | 
				
			|||
PATH=/sbin:/usr/sbin:/bin:/usr/bin | 
				
			|||
case "$1" in | 
				
			|||
    pre) | 
				
			|||
        STATUS=$(/usr/local/bin/socknix.py --socket /tmp/sleep.socket -m SUSPEND) | 
				
			|||
        if [ "$STATUS" != "OK" ]; then | 
				
			|||
            exit 1 | 
				
			|||
        fi | 
				
			|||
    ;; | 
				
			|||
    post) | 
				
			|||
        STATUS=$(/usr/local/bin/socknix.py --socket /tmp/sleep.socket -m AWAIKEN) | 
				
			|||
        if [ "$STATUS" != "OK" ]; then | 
				
			|||
            exit 1 | 
				
			|||
        fi | 
				
			|||
    ;; | 
				
			|||
esac | 
				
			|||
 | 
				
			|||
exit 0 | 
				
			|||
@ -1,3 +0,0 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
# Your script logic here | 
				
			|||
echo "Connection received on Unix socket" >> /var/log/exampleDvd.log | 
				
			|||
@ -1,9 +1,9 @@ | 
				
			|||
[Unit] | 
				
			|||
Description=Example Unix Socket | 
				
			|||
Description=Socket for DVD events (Sono) | 
				
			|||
Requires=dvd-socket-sono.service | 
				
			|||
 | 
				
			|||
[Socket] | 
				
			|||
ListenStream=/tmp/dvd-Sono-Socket.socket | 
				
			|||
Accept=false | 
				
			|||
ListenStream=/run/dvd-Sono-Socket.socket | 
				
			|||
 | 
				
			|||
[Install] | 
				
			|||
WantedBy=sockets.target | 
				
			|||
 | 
				
			|||
@ -1,93 +0,0 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
 | 
				
			|||
ACTION=$1 | 
				
			|||
DEVNAME=$2 | 
				
			|||
 | 
				
			|||
SOCKET_PATH="/tmp/usb-Sono-Socket.socket" | 
				
			|||
LOG_FILE="/tmp/tmpDvd.log" | 
				
			|||
DEBOUNCE_FILE="/tmp/dvd_debounce_$(echo $DEVNAME | tr '/' '-')" | 
				
			|||
DEBOUNCE_TIME=10  # seconds | 
				
			|||
 | 
				
			|||
current_time=$(date +%s) | 
				
			|||
 | 
				
			|||
# Function to log messages | 
				
			|||
log_message() { | 
				
			|||
    local message=$1 | 
				
			|||
    echo $message | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
    echo $message >> $LOG_FILE | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# Function to get the size of the DVD | 
				
			|||
get_dvd_size() { | 
				
			|||
    udisksctl info -b $DEVNAME | grep 'Size:' | awk '{print $2}' | head -n 1 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# Function to check for debounce | 
				
			|||
check_debounce() { | 
				
			|||
    SIZE=$(get_dvd_size) | 
				
			|||
    if [SIZE -eq 0]; then | 
				
			|||
        ATTEMPTS=3 | 
				
			|||
        while [ "$SIZE" -eq 0 ] && [ $ATTEMPTS -gt 0 ]; do | 
				
			|||
            sleep 2 | 
				
			|||
            SIZE=$(get_dvd_size) | 
				
			|||
            log_message "Debounced: Retried size of $DEVNAME is $SIZE" | 
				
			|||
            ATTEMPTS=$((ATTEMPTS - 1)) | 
				
			|||
        done | 
				
			|||
    elif [SIZE -eq 0]; then | 
				
			|||
        log_message "exit on: Retried size of $DEVNAME is $SIZE" | 
				
			|||
        exit 0 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if [ -f "$DEBOUNCE_FILE" ]; then | 
				
			|||
        last_run_time=$(cat $DEBOUNCE_FILE) | 
				
			|||
        elapsed_time=$((current_time - last_run_time)) | 
				
			|||
        if [ $elapsed_time -lt $DEBOUNCE_TIME ]; then | 
				
			|||
            log_message "Debounced: $ACTION $DEVNAME" | 
				
			|||
            exit 0 | 
				
			|||
        fi | 
				
			|||
    fi | 
				
			|||
    echo $current_time > $DEBOUNCE_FILE | 
				
			|||
} | 
				
			|||
 | 
				
			|||
check_debounce | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
if [ "$ACTION" == "change" ]; then | 
				
			|||
    # Introduce a delay before checking the device | 
				
			|||
    sleep 2  # Wait for 2 seconds before proceeding | 
				
			|||
 | 
				
			|||
    # Check if the device is already mounted | 
				
			|||
    MOUNTED=$(lsblk -o MOUNTPOINT -nr $DEVNAME) | 
				
			|||
    if [ -z "$MOUNTED" ]; then | 
				
			|||
        # Device is not mounted, check if it is a raw DVD | 
				
			|||
        SIZE=$(get_dvd_size) | 
				
			|||
 | 
				
			|||
        # Debug output for size | 
				
			|||
        log_message "DEBUG: Initial size of $DEVNAME is $SIZE" | 
				
			|||
 | 
				
			|||
        # Retry logic for getting the size | 
				
			|||
        ATTEMPTS=3 | 
				
			|||
        while [ "$SIZE" -eq 0 ] && [ $ATTEMPTS -gt 0 ]; do | 
				
			|||
            sleep 2 | 
				
			|||
            SIZE=$(get_dvd_size) | 
				
			|||
            log_message "DEBUG: Retried size of $DEVNAME is $SIZE" | 
				
			|||
            ATTEMPTS=$((ATTEMPTS - 1)) | 
				
			|||
        done | 
				
			|||
 | 
				
			|||
        if [ "$SIZE" -gt 0 ]; then | 
				
			|||
            log_message "success insert raw DVD $DEVNAME" | 
				
			|||
        else | 
				
			|||
            # Proceed to mount the device | 
				
			|||
            udisksctl mount -b $DEVNAME | 
				
			|||
            if [ $? -eq 0 ]; then | 
				
			|||
                log_message "success mount $DEVNAME" | 
				
			|||
            else | 
				
			|||
                log_message "failed to mount $DEVNAME" | 
				
			|||
            fi | 
				
			|||
        fi | 
				
			|||
    else | 
				
			|||
        log_message "$DEVNAME is already mounted at $MOUNTED" | 
				
			|||
    fi | 
				
			|||
    log_message "success insert $DEVNAME" | 
				
			|||
fi | 
				
			|||
@ -0,0 +1,13 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
 | 
				
			|||
ACTION=$1 | 
				
			|||
DEVICE_NAME=$2 | 
				
			|||
SOCKET_PATH="/run/dvd-Sono-Socket.socket" | 
				
			|||
 | 
				
			|||
if [ "$ACTION" == "add" ]; then | 
				
			|||
    echo -n "success insert $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
    echo "success insert $DEVICE_NAME  with socat" >> /tmp/tmpDvd.log | 
				
			|||
elif [ "$ACTION" == "remove" ]; then | 
				
			|||
    echo -n "success eject $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
    echo "success eject $DEVICE_NAME" >> /tmp/tmpDvd.log | 
				
			|||
fi | 
				
			|||
@ -1,195 +0,0 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
# https://github.com/pollev/bash_progress_bar - See license at end of file | 
				
			|||
 | 
				
			|||
# Constants | 
				
			|||
CODE_SAVE_CURSOR="\033[s" | 
				
			|||
CODE_RESTORE_CURSOR="\033[u" | 
				
			|||
CODE_CURSOR_IN_SCROLL_AREA="\033[1A" | 
				
			|||
COLOR_FG="\e[30m" | 
				
			|||
COLOR_BG="\e[42m" | 
				
			|||
COLOR_BG_BLOCKED="\e[43m" | 
				
			|||
RESTORE_FG="\e[39m" | 
				
			|||
RESTORE_BG="\e[49m" | 
				
			|||
 | 
				
			|||
# Variables | 
				
			|||
PROGRESS_BLOCKED="false" | 
				
			|||
TRAPPING_ENABLED="false" | 
				
			|||
TRAP_SET="false" | 
				
			|||
 | 
				
			|||
CURRENT_NR_LINES=0 | 
				
			|||
 | 
				
			|||
setup_scroll_area() { | 
				
			|||
    # If trapping is enabled, we will want to activate it whenever we setup the scroll area and remove it when we break the scroll area | 
				
			|||
    if [ "$TRAPPING_ENABLED" = "true" ]; then | 
				
			|||
        trap_on_interrupt | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    lines=$(tput lines) | 
				
			|||
    CURRENT_NR_LINES=$lines | 
				
			|||
    let lines=$lines-1 | 
				
			|||
    # Scroll down a bit to avoid visual glitch when the screen area shrinks by one row | 
				
			|||
    log -en "\n" | 
				
			|||
 | 
				
			|||
    # Save cursor | 
				
			|||
    log -en "$CODE_SAVE_CURSOR" | 
				
			|||
    # Set scroll region (this will place the cursor in the top left) | 
				
			|||
    log -en "\033[0;${lines}r" | 
				
			|||
 | 
				
			|||
    # Restore cursor but ensure its inside the scrolling area | 
				
			|||
    log -en "$CODE_RESTORE_CURSOR" | 
				
			|||
    log -en "$CODE_CURSOR_IN_SCROLL_AREA" | 
				
			|||
 | 
				
			|||
    # Start empty progress bar | 
				
			|||
    draw_progress_bar 0 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
destroy_scroll_area() { | 
				
			|||
    lines=$(tput lines) | 
				
			|||
    # Save cursor | 
				
			|||
    log -en "$CODE_SAVE_CURSOR" | 
				
			|||
    # Set scroll region (this will place the cursor in the top left) | 
				
			|||
    log -en "\033[0;${lines}r" | 
				
			|||
 | 
				
			|||
    # Restore cursor but ensure its inside the scrolling area | 
				
			|||
    log -en "$CODE_RESTORE_CURSOR" | 
				
			|||
    log -en "$CODE_CURSOR_IN_SCROLL_AREA" | 
				
			|||
 | 
				
			|||
    # We are done so clear the scroll bar | 
				
			|||
    clear_progress_bar | 
				
			|||
 | 
				
			|||
    # Scroll down a bit to avoid visual glitch when the screen area grows by one row | 
				
			|||
    log -en "\n\n" | 
				
			|||
 | 
				
			|||
    # Once the scroll area is cleared, we want to remove any trap previously set. Otherwise, ctrl+c will exit our shell | 
				
			|||
    if [ "$TRAP_SET" = "true" ]; then | 
				
			|||
        trap - INT | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
draw_progress_bar() { | 
				
			|||
    percentage=$1 | 
				
			|||
    lines=$(tput lines) | 
				
			|||
    let lines=$lines | 
				
			|||
 | 
				
			|||
    # Check if the window has been resized. If so, reset the scroll area | 
				
			|||
    if [ "$lines" -ne "$CURRENT_NR_LINES" ]; then | 
				
			|||
        setup_scroll_area | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    # Save cursor | 
				
			|||
    log -en "$CODE_SAVE_CURSOR" | 
				
			|||
 | 
				
			|||
    # Move cursor position to last row | 
				
			|||
    log -en "\033[${lines};0f" | 
				
			|||
 | 
				
			|||
    # Clear progress bar | 
				
			|||
    tput el | 
				
			|||
 | 
				
			|||
    # Draw progress bar | 
				
			|||
    PROGRESS_BLOCKED="false" | 
				
			|||
    print_bar_text $percentage | 
				
			|||
 | 
				
			|||
    # Restore cursor position | 
				
			|||
    log -en "$CODE_RESTORE_CURSOR" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
block_progress_bar() { | 
				
			|||
    percentage=$1 | 
				
			|||
    lines=$(tput lines) | 
				
			|||
    let lines=$lines | 
				
			|||
    # Save cursor | 
				
			|||
    log -en "$CODE_SAVE_CURSOR" | 
				
			|||
 | 
				
			|||
    # Move cursor position to last row | 
				
			|||
    log -en "\033[${lines};0f" | 
				
			|||
 | 
				
			|||
    # Clear progress bar | 
				
			|||
    tput el | 
				
			|||
 | 
				
			|||
    # Draw progress bar | 
				
			|||
    PROGRESS_BLOCKED="true" | 
				
			|||
    print_bar_text $percentage | 
				
			|||
 | 
				
			|||
    # Restore cursor position | 
				
			|||
    log -en "$CODE_RESTORE_CURSOR" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
clear_progress_bar() { | 
				
			|||
    lines=$(tput lines) | 
				
			|||
    let lines=$lines | 
				
			|||
    # Save cursor | 
				
			|||
    log -en "$CODE_SAVE_CURSOR" | 
				
			|||
 | 
				
			|||
    # Move cursor position to last row | 
				
			|||
    log -en "\033[${lines};0f" | 
				
			|||
 | 
				
			|||
    # clear progress bar | 
				
			|||
    tput el | 
				
			|||
 | 
				
			|||
    # Restore cursor position | 
				
			|||
    log -en "$CODE_RESTORE_CURSOR" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
print_bar_text() { | 
				
			|||
    local percentage=$1 | 
				
			|||
    local cols=$(tput cols) | 
				
			|||
    let bar_size=$cols-17 | 
				
			|||
 | 
				
			|||
    local color="${COLOR_FG}${COLOR_BG}" | 
				
			|||
    if [ "$PROGRESS_BLOCKED" = "true" ]; then | 
				
			|||
        color="${COLOR_FG}${COLOR_BG_BLOCKED}" | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    # Prepare progress bar | 
				
			|||
    let complete_size=($bar_size*$percentage)/100 | 
				
			|||
    let remainder_size=$bar_size-$complete_size | 
				
			|||
    progress_bar=$(log -ne "["; log -en "${color}"; printf_new "#" $complete_size; log -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder_size; log -ne "]"); | 
				
			|||
 | 
				
			|||
    # Print progress bar | 
				
			|||
    log -ne " Progress ${percentage}% ${progress_bar}" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
enable_trapping() { | 
				
			|||
    TRAPPING_ENABLED="true" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
trap_on_interrupt() { | 
				
			|||
    # If this function is called, we setup an interrupt handler to cleanup the progress bar | 
				
			|||
    TRAP_SET="true" | 
				
			|||
    trap cleanup_on_interrupt INT | 
				
			|||
} | 
				
			|||
 | 
				
			|||
cleanup_on_interrupt() { | 
				
			|||
    destroy_scroll_area | 
				
			|||
    exit | 
				
			|||
} | 
				
			|||
 | 
				
			|||
printf_new() { | 
				
			|||
    str=$1 | 
				
			|||
    num=$2 | 
				
			|||
    v=$(printf "%-${num}s" "$str") | 
				
			|||
    log -ne "${v// /$str}" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
 | 
				
			|||
# SPDX-License-Identifier: MIT | 
				
			|||
# | 
				
			|||
# Copyright (c) 2018--2020 Polle Vanhoof | 
				
			|||
# | 
				
			|||
# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			|||
# of this software and associated documentation files (the "Software"), to deal | 
				
			|||
# in the Software without restriction, including without limitation the rights | 
				
			|||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			|||
# copies of the Software, and to permit persons to whom the Software is | 
				
			|||
# furnished to do so, subject to the following conditions: | 
				
			|||
# | 
				
			|||
# The above copyright notice and this permission notice shall be included in all | 
				
			|||
# copies or substantial portions of the Software. | 
				
			|||
# | 
				
			|||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			|||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			|||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			|||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			|||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			|||
# SOFTWARE. | 
				
			|||
@ -1,25 +0,0 @@ | 
				
			|||
#!/usr/bin/python3 | 
				
			|||
 | 
				
			|||
# Author: Ali Hatami Tajik (info@alihatamitajik.ir) | 
				
			|||
# Date:   08 May 2023 | 
				
			|||
 | 
				
			|||
import socket | 
				
			|||
import argparse | 
				
			|||
 | 
				
			|||
parser = argparse.ArgumentParser(prog='socknix.py',  | 
				
			|||
                                 description='Sends specified massage to unix socket specified prints out the answer of the server in the stdout.') | 
				
			|||
                                 | 
				
			|||
parser.add_argument('--socket', '-s', required=True,  | 
				
			|||
                    help='address of the UNIX socket in the filesystem') | 
				
			|||
parser.add_argument('--message', '-m') | 
				
			|||
parser.add_argument('--buffer', '-b', type=int, default=1024) | 
				
			|||
 | 
				
			|||
args = parser.parse_args() | 
				
			|||
 | 
				
			|||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 
				
			|||
s.connect(args.socket) | 
				
			|||
if args.message: | 
				
			|||
    s.send(args.message.encode()) | 
				
			|||
else: | 
				
			|||
    s.send(input().encode()) | 
				
			|||
print(s.recv(args.buffer).decode(), end='') | 
				
			|||
@ -1,400 +0,0 @@ | 
				
			|||
#!/usr/bin/env bash | 
				
			|||
 | 
				
			|||
# DESC: Handler for unexpected errors | 
				
			|||
# ARGS: $1 (optional): Exit code (defaults to 1) | 
				
			|||
# OUTS: None | 
				
			|||
function script_trap_err() { | 
				
			|||
    local exit_code=1 | 
				
			|||
 | 
				
			|||
    # Disable the error trap handler to prevent potential recursion | 
				
			|||
    trap - ERR | 
				
			|||
 | 
				
			|||
    # Consider any further errors non-fatal to ensure we run to completion | 
				
			|||
    set +o errexit | 
				
			|||
    set +o pipefail | 
				
			|||
 | 
				
			|||
    # Validate any provided exit code | 
				
			|||
    if [[ ${1-} =~ ^[0-9]+$ ]]; then | 
				
			|||
        exit_code="$1" | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    # Output debug data if in Cron mode | 
				
			|||
    if [[ -n ${cron-} ]]; then | 
				
			|||
        # Restore original file output descriptors | 
				
			|||
        if [[ -n ${script_output-} ]]; then | 
				
			|||
            exec 1>&3 2>&4 | 
				
			|||
        fi | 
				
			|||
 | 
				
			|||
        # Print basic debugging information | 
				
			|||
        printf '%b\n' "$ta_none" | 
				
			|||
        printf '***** Abnormal termination of script *****\n' | 
				
			|||
        printf 'Script Path:            %s\n' "$script_path" | 
				
			|||
        printf 'Script Parameters:      %s\n' "$script_params" | 
				
			|||
        printf 'Script Exit Code:       %s\n' "$exit_code" | 
				
			|||
 | 
				
			|||
        # Print the script log if we have it. It's possible we may not if we | 
				
			|||
        # failed before we even called cron_init(). This can happen if bad | 
				
			|||
        # parameters were passed to the script so we bailed out very early. | 
				
			|||
        if [[ -n ${script_output-} ]]; then | 
				
			|||
            # shellcheck disable=SC2312 | 
				
			|||
            printf 'Script Output:\n\n%s' "$(cat "$script_output")" | 
				
			|||
        else | 
				
			|||
            printf 'Script Output:          None (failed before log init)\n' | 
				
			|||
        fi | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    # Exit with failure status | 
				
			|||
    exit "$exit_code" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Handler for exiting the script | 
				
			|||
# ARGS: None | 
				
			|||
# OUTS: None | 
				
			|||
function script_trap_exit() { | 
				
			|||
    cd "$orig_cwd" | 
				
			|||
 | 
				
			|||
    # Remove Cron mode script log | 
				
			|||
    if [[ -n ${cron-} && -f ${script_output-} ]]; then | 
				
			|||
        rm "$script_output" | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    # Remove script execution lock | 
				
			|||
    if [[ -d ${script_lock-} ]]; then | 
				
			|||
        rmdir "$script_lock" | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    # Restore terminal colours | 
				
			|||
    printf '%b' "$ta_none" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Exit script with the given message | 
				
			|||
# ARGS: $1 (required): Message to print on exit | 
				
			|||
#       $2 (optional): Exit code (defaults to 0) | 
				
			|||
# OUTS: None | 
				
			|||
# NOTE: The convention used in this script for exit codes is: | 
				
			|||
#       0: Normal exit | 
				
			|||
#       1: Abnormal exit due to external error | 
				
			|||
#       2: Abnormal exit due to script error | 
				
			|||
function script_exit() { | 
				
			|||
    if [[ $# -eq 1 ]]; then | 
				
			|||
        printf '%s\n' "$1" | 
				
			|||
        exit 0 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if [[ ${2-} =~ ^[0-9]+$ ]]; then | 
				
			|||
        printf '%b\n' "$1" | 
				
			|||
        # If we've been provided a non-zero exit code run the error trap | 
				
			|||
        if [[ $2 -ne 0 ]]; then | 
				
			|||
            script_trap_err "$2" | 
				
			|||
        else | 
				
			|||
            exit 0 | 
				
			|||
        fi | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    script_exit 'Missing required argument to script_exit()!' 2 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Generic script initialisation | 
				
			|||
# ARGS: $@ (optional): Arguments provided to the script | 
				
			|||
# OUTS: $orig_cwd: The current working directory when the script was run | 
				
			|||
#       $script_path: The full path to the script | 
				
			|||
#       $script_dir: The directory path of the script | 
				
			|||
#       $script_name: The file name of the script | 
				
			|||
#       $script_params: The original parameters provided to the script | 
				
			|||
#       $ta_none: The ANSI control code to reset all text attributes | 
				
			|||
# NOTE: $script_path only contains the path that was used to call the script | 
				
			|||
#       and will not resolve any symlinks which may be present in the path. | 
				
			|||
#       You can use a tool like realpath to obtain the "true" path. The same | 
				
			|||
#       caveat applies to both the $script_dir and $script_name variables. | 
				
			|||
# shellcheck disable=SC2034 | 
				
			|||
function script_init() { | 
				
			|||
    # Useful variables | 
				
			|||
    readonly orig_cwd="$PWD" | 
				
			|||
    readonly script_params="$*" | 
				
			|||
    readonly script_path="${BASH_SOURCE[1]}" | 
				
			|||
    script_dir="$(dirname "$script_path")" | 
				
			|||
    script_name="$(basename "$script_path")" | 
				
			|||
    readonly script_dir script_name | 
				
			|||
 | 
				
			|||
    # Important to always set as we use it in the exit handler | 
				
			|||
    # shellcheck disable=SC2155 | 
				
			|||
    readonly ta_none="$(tput sgr0 2> /dev/null || true)" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Initialise colour variables | 
				
			|||
# ARGS: None | 
				
			|||
# OUTS: Read-only variables with ANSI control codes | 
				
			|||
# NOTE: If --no-colour was set the variables will be empty. The output of the | 
				
			|||
#       $ta_none variable after each tput is redundant during normal execution, | 
				
			|||
#       but ensures the terminal output isn't mangled when running with xtrace. | 
				
			|||
# shellcheck disable=SC2034,SC2155 | 
				
			|||
function colour_init() { | 
				
			|||
    if [[ -z ${no_colour-} ]]; then | 
				
			|||
        # Text attributes | 
				
			|||
        readonly ta_bold="$(tput bold 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly ta_uscore="$(tput smul 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly ta_blink="$(tput blink 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly ta_reverse="$(tput rev 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly ta_conceal="$(tput invis 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
 | 
				
			|||
        # Foreground codes | 
				
			|||
        readonly fg_black="$(tput setaf 0 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly fg_blue="$(tput setaf 4 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly fg_cyan="$(tput setaf 6 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly fg_green="$(tput setaf 2 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly fg_magenta="$(tput setaf 5 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly fg_red="$(tput setaf 1 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly fg_white="$(tput setaf 7 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly fg_yellow="$(tput setaf 3 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
 | 
				
			|||
        # Background codes | 
				
			|||
        readonly bg_black="$(tput setab 0 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly bg_blue="$(tput setab 4 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly bg_cyan="$(tput setab 6 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly bg_green="$(tput setab 2 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly bg_magenta="$(tput setab 5 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly bg_red="$(tput setab 1 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly bg_white="$(tput setab 7 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
        readonly bg_yellow="$(tput setab 3 2> /dev/null || true)" | 
				
			|||
        printf '%b' "$ta_none" | 
				
			|||
    else | 
				
			|||
        # Text attributes | 
				
			|||
        readonly ta_bold='' | 
				
			|||
        readonly ta_uscore='' | 
				
			|||
        readonly ta_blink='' | 
				
			|||
        readonly ta_reverse='' | 
				
			|||
        readonly ta_conceal='' | 
				
			|||
 | 
				
			|||
        # Foreground codes | 
				
			|||
        readonly fg_black='' | 
				
			|||
        readonly fg_blue='' | 
				
			|||
        readonly fg_cyan='' | 
				
			|||
        readonly fg_green='' | 
				
			|||
        readonly fg_magenta='' | 
				
			|||
        readonly fg_red='' | 
				
			|||
        readonly fg_white='' | 
				
			|||
        readonly fg_yellow='' | 
				
			|||
 | 
				
			|||
        # Background codes | 
				
			|||
        readonly bg_black='' | 
				
			|||
        readonly bg_blue='' | 
				
			|||
        readonly bg_cyan='' | 
				
			|||
        readonly bg_green='' | 
				
			|||
        readonly bg_magenta='' | 
				
			|||
        readonly bg_red='' | 
				
			|||
        readonly bg_white='' | 
				
			|||
        readonly bg_yellow='' | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Initialise Cron mode | 
				
			|||
# ARGS: None | 
				
			|||
# OUTS: $script_output: Path to the file stdout & stderr was redirected to | 
				
			|||
function cron_init() { | 
				
			|||
    if [[ -n ${cron-} ]]; then | 
				
			|||
        # Redirect all output to a temporary file | 
				
			|||
        script_output="$(mktemp --tmpdir "$script_name".XXXXX)" | 
				
			|||
        readonly script_output | 
				
			|||
        exec 3>&1 4>&2 1> "$script_output" 2>&1 | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Acquire script lock | 
				
			|||
# ARGS: $1 (optional): Scope of script execution lock (system or user) | 
				
			|||
# OUTS: $script_lock: Path to the directory indicating we have the script lock | 
				
			|||
# NOTE: This lock implementation is extremely simple but should be reliable | 
				
			|||
#       across all platforms. It does *not* support locking a script with | 
				
			|||
#       symlinks or multiple hardlinks as there's no portable way of doing so. | 
				
			|||
#       If the lock was acquired it's automatically released on script exit. | 
				
			|||
function lock_init() { | 
				
			|||
    local lock_dir | 
				
			|||
    if [[ $1 = 'system' ]]; then | 
				
			|||
        lock_dir="/tmp/$script_name.lock" | 
				
			|||
    elif [[ $1 = 'user' ]]; then | 
				
			|||
        lock_dir="/tmp/$script_name.$UID.lock" | 
				
			|||
    else | 
				
			|||
        script_exit 'Missing or invalid argument to lock_init()!' 2 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if mkdir "$lock_dir" 2> /dev/null; then | 
				
			|||
        readonly script_lock="$lock_dir" | 
				
			|||
        verbose_print "Acquired script lock: $script_lock" | 
				
			|||
    else | 
				
			|||
        script_exit "Unable to acquire script lock: $lock_dir" 1 | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Pretty print the provided string | 
				
			|||
# ARGS: $1 (required): Message to print (defaults to a green foreground) | 
				
			|||
#       $2 (optional): Colour to print the message with. This can be an ANSI | 
				
			|||
#                      escape code or one of the prepopulated colour variables. | 
				
			|||
#       $3 (optional): Set to any value to not append a new line to the message | 
				
			|||
# OUTS: None | 
				
			|||
function pretty_print() { | 
				
			|||
    if [[ $# -lt 1 ]]; then | 
				
			|||
        script_exit 'Missing required argument to pretty_print()!' 2 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if [[ -z ${no_colour-} ]]; then | 
				
			|||
        if [[ -n ${2-} ]]; then | 
				
			|||
            printf '%b' "$2" | 
				
			|||
        else | 
				
			|||
            printf '%b' "$fg_green" | 
				
			|||
        fi | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    # Print message & reset text attributes | 
				
			|||
    if [[ -n ${3-} ]]; then | 
				
			|||
        printf '%s%b' "$1" "$ta_none" | 
				
			|||
    else | 
				
			|||
        printf '%s%b\n' "$1" "$ta_none" | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Only pretty_print() the provided string if verbose mode is enabled | 
				
			|||
# ARGS: $@ (required): Passed through to pretty_print() function | 
				
			|||
# OUTS: None | 
				
			|||
function verbose_print() { | 
				
			|||
    if [[ -n ${verbose-} ]]; then | 
				
			|||
        pretty_print "$@" | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Combines two path variables and removes any duplicates | 
				
			|||
# ARGS: $1 (required): Path(s) to join with the second argument | 
				
			|||
#       $2 (optional): Path(s) to join with the first argument | 
				
			|||
# OUTS: $build_path: The constructed path | 
				
			|||
# NOTE: Heavily inspired by: https://unix.stackexchange.com/a/40973 | 
				
			|||
function build_path() { | 
				
			|||
    if [[ $# -lt 1 ]]; then | 
				
			|||
        script_exit 'Missing required argument to build_path()!' 2 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    local new_path path_entry temp_path | 
				
			|||
 | 
				
			|||
    temp_path="$1:" | 
				
			|||
    if [[ -n ${2-} ]]; then | 
				
			|||
        temp_path="$temp_path$2:" | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    new_path= | 
				
			|||
    while [[ -n $temp_path ]]; do | 
				
			|||
        path_entry="${temp_path%%:*}" | 
				
			|||
        case "$new_path:" in | 
				
			|||
            *:"$path_entry":*) ;; | 
				
			|||
            *) | 
				
			|||
                new_path="$new_path:$path_entry" | 
				
			|||
                ;; | 
				
			|||
        esac | 
				
			|||
        temp_path="${temp_path#*:}" | 
				
			|||
    done | 
				
			|||
 | 
				
			|||
    # shellcheck disable=SC2034 | 
				
			|||
    build_path="${new_path#:}" | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Check a binary exists in the search path | 
				
			|||
# ARGS: $1 (required): Name of the binary to test for existence | 
				
			|||
#       $2 (optional): Set to any value to treat failure as a fatal error | 
				
			|||
# OUTS: None | 
				
			|||
function check_binary() { | 
				
			|||
    if [[ $# -lt 1 ]]; then | 
				
			|||
        script_exit 'Missing required argument to check_binary()!' 2 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if ! command -v "$1" > /dev/null 2>&1; then | 
				
			|||
        if [[ -n ${2-} ]]; then | 
				
			|||
            script_exit "Missing dependency: Couldn't locate $1." 1 | 
				
			|||
        else | 
				
			|||
            verbose_print "Missing dependency: $1" "${fg_red-}" | 
				
			|||
            return 1 | 
				
			|||
        fi | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    verbose_print "Found dependency: $1" | 
				
			|||
    return 0 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Validate we have superuser access as root (via sudo if requested) | 
				
			|||
# ARGS: $1 (optional): Set to any value to not attempt root access via sudo | 
				
			|||
# OUTS: None | 
				
			|||
function check_superuser() { | 
				
			|||
    local superuser | 
				
			|||
    if [[ $EUID -eq 0 ]]; then | 
				
			|||
        superuser=true | 
				
			|||
    elif [[ -z ${1-} ]]; then | 
				
			|||
        # shellcheck disable=SC2310 | 
				
			|||
        if check_binary sudo; then | 
				
			|||
            verbose_print 'Sudo: Updating cached credentials ...' | 
				
			|||
            if ! sudo -v; then | 
				
			|||
                verbose_print "Sudo: Couldn't acquire credentials ..." \ | 
				
			|||
                    "${fg_red-}" | 
				
			|||
            else | 
				
			|||
                local test_euid | 
				
			|||
                test_euid="$(sudo -H -- "$BASH" -c 'printf "%s" "$EUID"')" | 
				
			|||
                if [[ $test_euid -eq 0 ]]; then | 
				
			|||
                    superuser=true | 
				
			|||
                fi | 
				
			|||
            fi | 
				
			|||
        fi | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if [[ -z ${superuser-} ]]; then | 
				
			|||
        verbose_print 'Unable to acquire superuser credentials.' "${fg_red-}" | 
				
			|||
        return 1 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    verbose_print 'Successfully acquired superuser credentials.' | 
				
			|||
    return 0 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# DESC: Run the requested command as root (via sudo if requested) | 
				
			|||
# ARGS: $1 (optional): Set to zero to not attempt execution via sudo | 
				
			|||
#       $@ (required): Passed through for execution as root user | 
				
			|||
# OUTS: None | 
				
			|||
function run_as_root() { | 
				
			|||
    if [[ $# -eq 0 ]]; then | 
				
			|||
        script_exit 'Missing required argument to run_as_root()!' 2 | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if [[ ${1-} =~ ^0$ ]]; then | 
				
			|||
        local skip_sudo=true | 
				
			|||
        shift | 
				
			|||
    fi | 
				
			|||
 | 
				
			|||
    if [[ $EUID -eq 0 ]]; then | 
				
			|||
        "$@" | 
				
			|||
    elif [[ -z ${skip_sudo-} ]]; then | 
				
			|||
        sudo -H -- "$@" | 
				
			|||
    else | 
				
			|||
        script_exit "Unable to run requested command as root: $*" 1 | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
function log () { | 
				
			|||
    if [[ $_V -eq 1 ]]; then | 
				
			|||
        echo "$@" | 
				
			|||
    fi | 
				
			|||
} | 
				
			|||
 | 
				
			|||
# vim: syntax=sh cc=80 tw=79 ts=4 sw=4 sts=4 et sr | 
				
			|||
@ -1,3 +0,0 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
# Your script logic here | 
				
			|||
echo "Connection received on Unix socket" >> /var/log/example.log | 
				
			|||
@ -1,9 +1,9 @@ | 
				
			|||
[Unit] | 
				
			|||
Description=Example Unix Socket | 
				
			|||
Description=Socket for USB events (Sono) | 
				
			|||
Requires=usb-socket-sono.service | 
				
			|||
 | 
				
			|||
[Socket] | 
				
			|||
ListenStream=/tmp/usb-Sono-Socket.socket | 
				
			|||
Accept=false | 
				
			|||
ListenStream=/run/usb-Sono-Socket.socket | 
				
			|||
 | 
				
			|||
[Install] | 
				
			|||
WantedBy=sockets.target | 
				
			|||
 | 
				
			|||
@ -1,37 +0,0 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
 | 
				
			|||
# Get the action (add or remove) and the device name from the udev rule | 
				
			|||
ACTION=$1 | 
				
			|||
 | 
				
			|||
# Get the device name from the udev rule | 
				
			|||
DEVICE_NAME=$2 | 
				
			|||
 | 
				
			|||
SOCKET_PATH="/tmp/usb-Sono-Socket.socket" | 
				
			|||
 | 
				
			|||
if [ "$ACTION" == "add" ]; then | 
				
			|||
    # Mount the device | 
				
			|||
    udisksctl mount -b "$DEVICE_NAME" | 
				
			|||
 | 
				
			|||
    # Check if the mount was successful | 
				
			|||
    if [ $? -eq 0 ]; then | 
				
			|||
        # Write a message to the socket | 
				
			|||
        echo "success connect $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
	echo "success connect $DEVICE_NAME" >> /tmp/tmp.log | 
				
			|||
    else | 
				
			|||
        echo "failure connect $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
	echo "failure connect $DEVICE_NAME" >> /tmp/tmp.log | 
				
			|||
    fi | 
				
			|||
elif [ "$ACTION" == "remove" ]; then | 
				
			|||
    # Unmount the device | 
				
			|||
    udisksctl unmount -b "$DEVICE_NAME" | 
				
			|||
 | 
				
			|||
    # Check if the unmount was successful | 
				
			|||
    if [ $? -eq 0 ]; then | 
				
			|||
        # Write a message to the socket | 
				
			|||
        echo "success disconnect $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
	echo "success disconnect $DEVICE_NAME" >> /tmp/tmp.log | 
				
			|||
    else | 
				
			|||
        echo "failure disconnect $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
	echo "failure disconnect $DEVICE_NAME" >> /tmp/tmp.log | 
				
			|||
    fi | 
				
			|||
fi | 
				
			|||
@ -0,0 +1,13 @@ | 
				
			|||
#!/bin/bash | 
				
			|||
 | 
				
			|||
ACTION=$1 | 
				
			|||
DEVICE_NAME=$2 | 
				
			|||
SOCKET_PATH="/run/usb-Sono-Socket.socket" | 
				
			|||
 | 
				
			|||
if [ "$ACTION" == "add" ]; then | 
				
			|||
    echo -n "success connect $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
    echo "success connect $DEVICE_NAME  with socat" >> /tmp/tmpUsb.log | 
				
			|||
elif [ "$ACTION" == "remove" ]; then | 
				
			|||
    echo -n "success disconnect $DEVICE_NAME" | socat - UNIX-CONNECT:$SOCKET_PATH | 
				
			|||
    echo "success disconnect $DEVICE_NAME" >> /tmp/tmpUsb.log | 
				
			|||
fi | 
				
			|||
@ -1,46 +0,0 @@ | 
				
			|||
#!/usr/bin/env python3 | 
				
			|||
 | 
				
			|||
"""Change mouse script | 
				
			|||
 | 
				
			|||
Whenever a mouse is changed to the system, this script will be executed. These | 
				
			|||
are the rules: | 
				
			|||
    + All regular mouses should connect to the "Virtual core pointer" of the | 
				
			|||
      system | 
				
			|||
    + Touchpanel should be connected to the master "touch-pointer" | 
				
			|||
 | 
				
			|||
NOTE: if master `touch` is not present in the system, the script will create a | 
				
			|||
      master `touch` itself and hides the pointer. eGalax device input will be | 
				
			|||
      attached to this master. TODO: make master's cursor invisible. | 
				
			|||
       | 
				
			|||
      Currently we'll use xinput command-line application, but, It is possible | 
				
			|||
      to write a specified c program that uses Xlib efficiently. | 
				
			|||
       | 
				
			|||
    Steps: | 
				
			|||
        + List all pointer as we don't know what pointer is added to the system | 
				
			|||
        + group them by their usage by name rules (As if the pointer is eGalax | 
				
			|||
          touch pointer it should be attached to the touch-pointer o.w. it | 
				
			|||
          should be attached to Vitual core pointer (OR trackball TODO)) | 
				
			|||
 | 
				
			|||
    utility functions to find and group pointers aer available in xutil module. | 
				
			|||
 | 
				
			|||
NOTE: In case of psyco adds and removes mouses with intervals smaller than run | 
				
			|||
    time of this code (which is not probabale at all) a lockfile is used so that | 
				
			|||
    only one instance of this code is running at a moment so no conflicts occur. | 
				
			|||
""" | 
				
			|||
 | 
				
			|||
import util.pointer as putil | 
				
			|||
import util.x as xutil | 
				
			|||
 | 
				
			|||
if __name__ == "__main__": | 
				
			|||
    """Configure Pointers | 
				
			|||
 | 
				
			|||
    Execution time: 140ms avg -> Tolarable | 
				
			|||
    """ | 
				
			|||
    v_core, touch_master, e_galax, pointers = putil.get_pointers_categorized() | 
				
			|||
    if e_galax: | 
				
			|||
        xutil.reattach(e_galax.id, touch_master.id) | 
				
			|||
    else: | 
				
			|||
        # TODO: disable touch? | 
				
			|||
        pass | 
				
			|||
    for p in pointers: | 
				
			|||
        xutil.reattach(p.id, v_core.id) | 
				
			|||
@ -1,3 +0,0 @@ | 
				
			|||
[DEFAULT] | 
				
			|||
MainDisplay=HDMI-3 | 
				
			|||
Policy=Mirror | 
				
			|||
@ -1,211 +0,0 @@ | 
				
			|||
"""Setup Monitor Script | 
				
			|||
 | 
				
			|||
Author: Ali Hatami Tajik [hatam](mailto:a.hatam008@gmail.com) | 
				
			|||
Date:   2023 Mar 04 | 
				
			|||
 | 
				
			|||
    This script should be used whenever a change happen in the rdm system, I  | 
				
			|||
    guess! But as I investigate a rdm change event will happen many time in case | 
				
			|||
    of addition or removal of a monitor auto configuration is done by the  | 
				
			|||
    org.mate.SettingsDaemon.plugins.xrandr deamon. I'll searching for a way to | 
				
			|||
    change that event to run our specified script. | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    This script will do the following: | 
				
			|||
        1. List currently available monitors | 
				
			|||
        2. Map those to config file | 
				
			|||
        3. Replace default values for missing configs | 
				
			|||
        4. Handle missing touch-screen/main monitor | 
				
			|||
                NOTE: In general we use at least two monitoers with our system. | 
				
			|||
                      One is for touch screen and one is for regular screen. In | 
				
			|||
                      case of one missing a callback is run to handle that | 
				
			|||
                      occurance (an event may send to the software or a temp log | 
				
			|||
                      file may be updated so the software adjust ifself  | 
				
			|||
                      correspondigly). | 
				
			|||
 | 
				
			|||
    Config files are in JSON format and spesify screen output name and their | 
				
			|||
    mode (xrandr resolution and stuff may be added but for now --auto/--prefered | 
				
			|||
    option is used). The format is: | 
				
			|||
         | 
				
			|||
""" | 
				
			|||
 | 
				
			|||
import Xlib.display | 
				
			|||
import Xlib.ext.randr | 
				
			|||
import configparser | 
				
			|||
from util import edit_distance | 
				
			|||
from util.egalax import get_egalax_drm_pure_name | 
				
			|||
import subprocess | 
				
			|||
from pathlib import Path | 
				
			|||
import os  | 
				
			|||
 | 
				
			|||
CONFIG_NAME = Path(os.path.dirname(os.path.realpath(__file__))) / "conf/desktop.conf" | 
				
			|||
XRANDR = "/usr/bin/xrandr" | 
				
			|||
 | 
				
			|||
def read_config(): | 
				
			|||
    """Reads config file of desktop setup | 
				
			|||
 | 
				
			|||
    This function will reeds the config file of desktop. Config file contins | 
				
			|||
    a default monitor port name which will be the main Monitor display. | 
				
			|||
 | 
				
			|||
    NOTE: eGalax devide will be detected automatically from the /dev mountings. | 
				
			|||
    """ | 
				
			|||
    config = configparser.ConfigParser() | 
				
			|||
    read = config.read(CONFIG_NAME) | 
				
			|||
    if not read: | 
				
			|||
        raise FileNotFoundError("Desktop config file not found") | 
				
			|||
    return config | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def all_connected_monitor(): | 
				
			|||
    """Generates all connected monitors | 
				
			|||
 | 
				
			|||
    as a tuple of (atom name: str, width, height) | 
				
			|||
    """ | 
				
			|||
    display = Xlib.display.Display() | 
				
			|||
    root = display.screen().root | 
				
			|||
    for m in root.xrandr_get_monitors().monitors: | 
				
			|||
        yield ( | 
				
			|||
            display.get_atom_name(m.name), | 
				
			|||
            m.width_in_pixels, | 
				
			|||
            m.height_in_pixels, | 
				
			|||
        ) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_edid_name(drm_name: str): | 
				
			|||
    """Change eGalax DRM name to atom name | 
				
			|||
     | 
				
			|||
    This function is very sensitive to kernel version and might not work | 
				
			|||
    with some kernels. | 
				
			|||
    """ | 
				
			|||
    card_num, name = drm_name[4:].split("-", maxsplit=1) | 
				
			|||
    first, second = name.rsplit("-", maxsplit=1) | 
				
			|||
    return first + "-" + card_num + "-" + second | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def prepare_monitors(config): | 
				
			|||
    """Prepare monitor names | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    Rules: | 
				
			|||
        - Use default monitor port as the main monitor if it is connected | 
				
			|||
            - If default monitor is not connected then use a monitor with | 
				
			|||
              minimum edit distance. | 
				
			|||
 | 
				
			|||
        - Use eGalax as touchpanel and if it's not connected return None. | 
				
			|||
 | 
				
			|||
        - other connected monitors will be returned as a list | 
				
			|||
 | 
				
			|||
    each monitor is returen as a | 
				
			|||
 | 
				
			|||
    Returns | 
				
			|||
        tuple: of | 
				
			|||
            - main monitor -> tuple | None if no monitor available other than | 
				
			|||
              touchpanel | 
				
			|||
            - touchpanel   -> tuple | None if touchpanel did not exist | 
				
			|||
            - other        -> list: this list may be empty if there is no other | 
				
			|||
                                    monitor connected | 
				
			|||
    """ | 
				
			|||
    main = config["DEFAULT"]["MainDisplay"] | 
				
			|||
    all_monitors = list(all_connected_monitor()) | 
				
			|||
    egalax_drm = get_egalax_drm_pure_name() | 
				
			|||
    egalax_name = get_edid_name(egalax_drm) if egalax_drm else None | 
				
			|||
    egalax_monitor = None | 
				
			|||
    main_monitor = None | 
				
			|||
    for mon in all_monitors: | 
				
			|||
        if egalax_name == mon[0]: | 
				
			|||
            egalax_monitor = mon | 
				
			|||
        if main == mon[0]: | 
				
			|||
            main_monitor = mon | 
				
			|||
    if egalax_monitor: | 
				
			|||
        all_monitors.remove(egalax_monitor) | 
				
			|||
    if not main_monitor: | 
				
			|||
        try: | 
				
			|||
            min_monitor = min( | 
				
			|||
                all_monitors, | 
				
			|||
                key=lambda x: edit_distance(main, x, len(main), len(x)), | 
				
			|||
            ) | 
				
			|||
            main_monitor = min_monitor | 
				
			|||
        except: | 
				
			|||
            main_monitor = None | 
				
			|||
            assert len(all_monitors) == 0 | 
				
			|||
    if main_monitor: | 
				
			|||
        all_monitors.remove(main_monitor) | 
				
			|||
    return main_monitor, egalax_monitor, all_monitors | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def baseline(main, egalax): | 
				
			|||
    """Base of xrandr arguments | 
				
			|||
     | 
				
			|||
    Both main and egalax are monitor tuples mentioned in prepare_monitors""" | 
				
			|||
    if not main and not egalax: | 
				
			|||
        return [], None | 
				
			|||
    elif not main and egalax: | 
				
			|||
        return ["--output", egalax[0], "--primary",  | 
				
			|||
                "--mode", f"{egalax[1]}x{egalax[2]}"], None | 
				
			|||
    elif main and not egalax: | 
				
			|||
        return ["--output", main[0], "--primary", | 
				
			|||
                "--mode", f"{main[1]}x{main[2]}"], main[0] | 
				
			|||
    else: | 
				
			|||
        return ["--output", main[0], "--primary", | 
				
			|||
                "--mode", f"{main[1]}x{main[2]}", | 
				
			|||
                "--output", egalax[0], "--right-of", main[0], | 
				
			|||
                "--mode", f"{egalax[1]}x{egalax[1]}"], egalax[0] | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def mirror_displays(main, egalax, others: list): | 
				
			|||
    base, should_mirror = baseline(main, egalax) | 
				
			|||
    if should_mirror: | 
				
			|||
        for name, width, height in others: | 
				
			|||
            base.extend(["--output", name, "--mode", f"{width}x{height}", | 
				
			|||
                        "--same-as", main[0],  | 
				
			|||
                        "--scale-from", f"{main[1]}x{main[2]}"]) | 
				
			|||
    return base | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def off_displays(main, egalax, others: list): | 
				
			|||
    base, should_off = baseline(main, egalax) | 
				
			|||
    if should_off: | 
				
			|||
        for name, width, height in others: | 
				
			|||
                base.extend(["--output", name, "--off"]) | 
				
			|||
    return base | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def stand_alone_displays(main, egalax, others: list): | 
				
			|||
    base, rightmost = baseline(main, egalax) | 
				
			|||
    if rightmost: | 
				
			|||
        for name, width, height in others: | 
				
			|||
            base.extend(["--output", name, "--mode", f"{width}x{height}", | 
				
			|||
                        "--right-of", rightmost]) | 
				
			|||
            rightmost = name | 
				
			|||
    return base | 
				
			|||
 | 
				
			|||
 | 
				
			|||
POLICY = { | 
				
			|||
    'Off': off_displays, | 
				
			|||
    'Mirror': mirror_displays, | 
				
			|||
    'StandAlone': stand_alone_displays | 
				
			|||
} | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def config_xrandr(conf, main, egalax, others: list): | 
				
			|||
    """Executes xrandr with policy in the conf | 
				
			|||
     | 
				
			|||
    Policies: | 
				
			|||
        Policies are about monitors other than main and touch panel monitors. | 
				
			|||
        There are three supported policies: | 
				
			|||
            - Off: Disables other monitors (default policy if not in config) | 
				
			|||
            - Mirror: Mirror other displays from main monitor. | 
				
			|||
            - StandAlone: Each monitor is mapped to the right of each other | 
				
			|||
    """ | 
				
			|||
    try: | 
				
			|||
        policy = conf['DEFAULT']['Policy'] | 
				
			|||
    except: | 
				
			|||
        policy = 'Off' | 
				
			|||
    args = POLICY[policy](main, egalax, others) | 
				
			|||
    cmd = [XRANDR] + args | 
				
			|||
    subprocess.run(cmd) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
if __name__ == "__main__": | 
				
			|||
    conf = read_config() | 
				
			|||
    main, egalax, others = prepare_monitors(conf) | 
				
			|||
    config_xrandr(conf, main, egalax, others) | 
				
			|||
@ -1,64 +0,0 @@ | 
				
			|||
from abc import ABC, abstractmethod | 
				
			|||
from pyudev import Context, Monitor, MonitorObserver, Device | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class Handler(ABC): | 
				
			|||
    """Abstract Handler calss for device monitoring | 
				
			|||
 | 
				
			|||
    NOTE: No checking are done for overlaping filters and callback will be | 
				
			|||
    even by multiple handlers. | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        ABC: Abstract Base Class, provides abstract method functionality and | 
				
			|||
             readability. | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    def __init__(self, filter) -> None: | 
				
			|||
        """Initiate a monitor observer and applies `filter` if any provided | 
				
			|||
 | 
				
			|||
        Args: | 
				
			|||
            filter (_type_): _description_ | 
				
			|||
        """ | 
				
			|||
        monitor = Monitor.from_netlink(Context()) | 
				
			|||
        if filter: | 
				
			|||
            monitor.filter_by(filter) | 
				
			|||
        self.observer = MonitorObserver(monitor, callback=self.handler) | 
				
			|||
        self.observer.start() | 
				
			|||
 | 
				
			|||
    @abstractmethod | 
				
			|||
    def callback(self, device: Device): | 
				
			|||
        """Callback | 
				
			|||
 | 
				
			|||
        This method must be implemented by child calsses. This method is | 
				
			|||
        responsible for further managments of the devices related to its filter. | 
				
			|||
 | 
				
			|||
        Args: | 
				
			|||
            device (pyudev.Device): device passed by observer through handler | 
				
			|||
        """ | 
				
			|||
        raise NotImplemented("Callback MUST be implemented") | 
				
			|||
 | 
				
			|||
    def handler(self, device): | 
				
			|||
        """wrapper around callback implemented | 
				
			|||
 | 
				
			|||
        Args: | 
				
			|||
            device (pyudev.Device): modified device passed by self.observer | 
				
			|||
        """ | 
				
			|||
        self.callback(device) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class MouseHandler(Handler): | 
				
			|||
    def __init__(self) -> None: | 
				
			|||
        """Initiate UsbHanlder | 
				
			|||
 | 
				
			|||
        Initialization contains two major steps. First it would do a | 
				
			|||
        configuration for currently available devices and then it would wait for | 
				
			|||
        USB udev events to reconfigure the settings. configurations would be | 
				
			|||
        done by (This part is not decided yet. it could be done by BASH SCRIPTS | 
				
			|||
        or we can invoke xinput binaries via python itself. a bash script | 
				
			|||
        solution would be benefitial since it can used as utility). | 
				
			|||
        """ | 
				
			|||
        # TODO: use somthing that only captures | 
				
			|||
        super().__init__("usb") | 
				
			|||
 | 
				
			|||
    def callback(self, device): | 
				
			|||
        print(device.action) | 
				
			|||
@ -1 +0,0 @@ | 
				
			|||
from .common import edit_distance, max_match | 
				
			|||
@ -1,63 +0,0 @@ | 
				
			|||
"""Common Utilities""" | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def max_match(a: str, b: str) -> str: | 
				
			|||
    """Maximum matching of intersection of pair of string | 
				
			|||
 | 
				
			|||
    This function will return the intersection of two strings from the start. | 
				
			|||
 | 
				
			|||
    Example: | 
				
			|||
        >>> a = "/sys/devices/folan/bahman" | 
				
			|||
        >>> b = "/sys/devices/fol/bahman" | 
				
			|||
        >>> max_match(a,b) | 
				
			|||
        '/sys/dedices/fol' | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        a (str): firsrt string | 
				
			|||
        b (str): second string | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
        str: intersection of two strings OR  None if one or both strings are | 
				
			|||
        empty or None | 
				
			|||
    """ | 
				
			|||
    i = 0 | 
				
			|||
 | 
				
			|||
    if not a or not b: | 
				
			|||
        return None | 
				
			|||
 | 
				
			|||
    if len(b) < len(a): | 
				
			|||
        a, b = b, a | 
				
			|||
 | 
				
			|||
    for c in a: | 
				
			|||
        if b[i] == c: | 
				
			|||
            i += 1 | 
				
			|||
        else: | 
				
			|||
            return a[0:i] | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def edit_distance(str1, str2, m, n): | 
				
			|||
    # If first string is empty, the only option is to | 
				
			|||
    # insert all characters of second string into first | 
				
			|||
    if m == 0: | 
				
			|||
        return n | 
				
			|||
 | 
				
			|||
    # If second string is empty, the only option is to | 
				
			|||
    # remove all characters of first string | 
				
			|||
    if n == 0: | 
				
			|||
        return m | 
				
			|||
 | 
				
			|||
    # If last characters of two strings are same, nothing | 
				
			|||
    # much to do. Ignore last characters and get count for | 
				
			|||
    # remaining strings. | 
				
			|||
    if str1[m - 1] == str2[n - 1]: | 
				
			|||
        return edit_distance(str1, str2, m - 1, n - 1) | 
				
			|||
 | 
				
			|||
    # If last characters are not same, consider all three | 
				
			|||
    # operations on last character of first string, recursively | 
				
			|||
    # compute minimum cost for all three operations and take | 
				
			|||
    # minimum of three values. | 
				
			|||
    return 1 + min( | 
				
			|||
        edit_distance(str1, str2, m, n - 1),  # Insert | 
				
			|||
        edit_distance(str1, str2, m - 1, n),  # Remove | 
				
			|||
        edit_distance(str1, str2, m - 1, n - 1),  # Replace | 
				
			|||
    ) | 
				
			|||
@ -1,80 +0,0 @@ | 
				
			|||
"""eGalax | 
				
			|||
 | 
				
			|||
This module is responsible for detecting the touchpanel. It would detect the | 
				
			|||
touchpannel's drm output and its overal status. | 
				
			|||
""" | 
				
			|||
 | 
				
			|||
from pathlib import Path | 
				
			|||
from .x import get_edid_dev_path | 
				
			|||
from .common import max_match | 
				
			|||
 | 
				
			|||
VENDOR_ID = "0EEF" | 
				
			|||
DEVICE_ID = "C000" | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_egalax_path() -> Path: | 
				
			|||
    """Get device path | 
				
			|||
 | 
				
			|||
    This function will return the path of the HID device related to the pannel. | 
				
			|||
    NOTE that it is not the path of the EDID but it can be extracted from it. | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
        Path: Path of the eGalax hid device OR None if device is not ceonnected | 
				
			|||
    """ | 
				
			|||
    query = "*" + VENDOR_ID + ":" + DEVICE_ID + "*" | 
				
			|||
    devices = list(Path("/sys/devices").rglob(query)) | 
				
			|||
    if devices: | 
				
			|||
        return devices[0] | 
				
			|||
    else: | 
				
			|||
        return None | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def is_egalax_connected() -> bool: | 
				
			|||
    """Checks if device is connected | 
				
			|||
 | 
				
			|||
    avaiability of the device is checked by existing a path of the device in the | 
				
			|||
    /sys/devices directory. | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
        bool: True if device is connected | 
				
			|||
    """ | 
				
			|||
    devpath = get_egalax_path() | 
				
			|||
    return bool(devpath) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_egalax_edid_path() -> Path: | 
				
			|||
    """return EDID path of touchpannel rdm | 
				
			|||
 | 
				
			|||
    This function will find intersection of the edid pathes and eGalax hid | 
				
			|||
    device and if this intersection and returns the maximum match. | 
				
			|||
 | 
				
			|||
    Runtime: 160ms on average -> Not efficient | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
        Path: edid path of eGalax OR None if not found or device is'nt connected | 
				
			|||
    """ | 
				
			|||
    egalax_dev = get_egalax_path() | 
				
			|||
    if not egalax_dev: | 
				
			|||
        return None | 
				
			|||
    max_dir = "/sys/devices" | 
				
			|||
    max_path = None | 
				
			|||
    for path in get_edid_dev_path(): | 
				
			|||
        base_dir = max_match(str(path), str(egalax_dev)) | 
				
			|||
        if len(max_dir) < len(base_dir): | 
				
			|||
            max_dir = base_dir | 
				
			|||
            max_path = path | 
				
			|||
    # TODO add sanity check (both edid and VENDOR:DEVICE are in that base) | 
				
			|||
    return max_path | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_egalax_drm_pure_name() -> str: | 
				
			|||
    """Extract DRM name form edid path | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
        str: pure drm name OR none if device is not found | 
				
			|||
    """ | 
				
			|||
    edid_path = get_egalax_edid_path() | 
				
			|||
    if edid_path: | 
				
			|||
        return str(edid_path.parent.stem) | 
				
			|||
    else: | 
				
			|||
        return None | 
				
			|||
@ -1,198 +0,0 @@ | 
				
			|||
import util.x as xutil | 
				
			|||
 | 
				
			|||
# Pointer states | 
				
			|||
SLAVE, MASTER, FLOATING = range(3) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class XInput: | 
				
			|||
    """Base XInput class | 
				
			|||
 | 
				
			|||
    Attributes: | 
				
			|||
        name (str): name of the input | 
				
			|||
        id (int): id of the input | 
				
			|||
        is_master (bool): True if device is master | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    def __init__(self, name, id, state) -> None: | 
				
			|||
        """Initializes the class with name, id and master status | 
				
			|||
 | 
				
			|||
        Args: | 
				
			|||
            name (str): name of the input. No processing is done on the name | 
				
			|||
            id (int): id of the input | 
				
			|||
            is_master (bool): master status of the input device | 
				
			|||
        """ | 
				
			|||
        self.name = name | 
				
			|||
        self.id = id | 
				
			|||
        self.state = state | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class Pointer(XInput): | 
				
			|||
    """Pointer class | 
				
			|||
 | 
				
			|||
    This class is a wrapper around xinput commandline --list output. | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    Attrs: | 
				
			|||
        is_master (bool): True if the pointer is a master pointer else False | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    def __init__(self, name, id, state) -> None: | 
				
			|||
        super().__init__(name, id, state) | 
				
			|||
 | 
				
			|||
    def __repr__(self) -> str: | 
				
			|||
        return f"<Pointer: {self.name}-{self.id}-{self.state}>" | 
				
			|||
 | 
				
			|||
    @property | 
				
			|||
    def slave(self): | 
				
			|||
        return self.state == SLAVE | 
				
			|||
 | 
				
			|||
    @property | 
				
			|||
    def master(self): | 
				
			|||
        return self.state == MASTER | 
				
			|||
 | 
				
			|||
    def floating(self): | 
				
			|||
        return self.state == FLOATING | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_short_pointer(id) -> Pointer: | 
				
			|||
    """Generates Pointer object corresponding to id (short attrs) | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        id (int): pointer id | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
        Pointer: pointer object with name, id and is_master props | 
				
			|||
 | 
				
			|||
    Rises: | 
				
			|||
        ValueError: if id is not reistered with xinput | 
				
			|||
        ValueError: if id is not a pointer id | 
				
			|||
    """ | 
				
			|||
    desc = xutil.get_list_short_with(id) | 
				
			|||
    name, props = desc.rsplit("id=", 1) | 
				
			|||
    if "pointer" in props: | 
				
			|||
        state = FLOATING | 
				
			|||
        if "master" in props: | 
				
			|||
            state = MASTER | 
				
			|||
        elif "slave" in props: | 
				
			|||
            state = SLAVE | 
				
			|||
        return Pointer(name.strip(), props.split(maxsplit=1)[0], state) | 
				
			|||
    else: | 
				
			|||
        raise TypeError(f"id[{id}] is not a pointer id") | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_ids_iter(): | 
				
			|||
    """xinput id generator | 
				
			|||
 | 
				
			|||
    Yields: | 
				
			|||
        int: id of xinput devices | 
				
			|||
    """ | 
				
			|||
    ids = xutil.get_ids() | 
				
			|||
    for id in ids: | 
				
			|||
        yield id | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_pointer_iter(is_short=True): | 
				
			|||
    """xinput pointers generator | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        is_short (bool, optional): if True generates short type pointers. | 
				
			|||
        Defaults to True. | 
				
			|||
 | 
				
			|||
    Yields: | 
				
			|||
        Pointer: xinput pointers | 
				
			|||
    """ | 
				
			|||
    for id in get_ids_iter(): | 
				
			|||
        if is_short: | 
				
			|||
            try: | 
				
			|||
                yield get_short_pointer(id) | 
				
			|||
            except TypeError as e: | 
				
			|||
                # ignore if the id is not pointer | 
				
			|||
                pass | 
				
			|||
            except e: | 
				
			|||
                # TODO: logging | 
				
			|||
                pass | 
				
			|||
        else: | 
				
			|||
            pass  # TODO | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_pointers(is_short=True): | 
				
			|||
    """Wraps pointers in `xinput --list` in Pointer class | 
				
			|||
 | 
				
			|||
    Creation of the pointer is done by getting the list description of | 
				
			|||
    each id. if the is_short arg is True, then short list description will be | 
				
			|||
    used which will provide the class `name`, `is_master` and `id` values. | 
				
			|||
 | 
				
			|||
    Getting this pointers is done by first calling `xinput --list --id-only` to | 
				
			|||
    get ids and then execute `xinput --list {id}` to get the description with | 
				
			|||
    less-complicated output compare to iterating over `xinput --list --short` | 
				
			|||
    line by line (--short option has some special characters that cause overhead | 
				
			|||
    to the system for processing them individually and per-case). | 
				
			|||
    """ | 
				
			|||
    pointers = [] | 
				
			|||
    for pointer in get_pointer_iter(is_short): | 
				
			|||
        pointers.append(pointer) | 
				
			|||
    return pointers | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def create_touch_master(): | 
				
			|||
    """_summary_ | 
				
			|||
 | 
				
			|||
    Raises: | 
				
			|||
        SystemError: If creation of touch pointer failed | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
        Pointer: pointer object corresponding to `touch` master | 
				
			|||
    """ | 
				
			|||
    touch = None | 
				
			|||
    xutil.create_master("touch") | 
				
			|||
    id = xutil.get_xi_id_by_name("touch pointer") | 
				
			|||
    if id: | 
				
			|||
        try: | 
				
			|||
            touch = get_short_pointer(id) | 
				
			|||
        except: | 
				
			|||
            raise SystemError( | 
				
			|||
                "touch pointer is not available. cannot create touch pointer" | 
				
			|||
            ) | 
				
			|||
    else: | 
				
			|||
        raise SystemError( | 
				
			|||
            "touch pointer is not available. cannot create touch pointer" | 
				
			|||
        ) | 
				
			|||
 | 
				
			|||
    # TODO configure cursor bitmap | 
				
			|||
    return touch | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_pointers_categorized(): | 
				
			|||
    """Categorized Pointers | 
				
			|||
 | 
				
			|||
    Categories: | 
				
			|||
        1. VCore: Pointer | 
				
			|||
        2. Touch Master: Pointer | 
				
			|||
        3. eGalax: Pointer | None | 
				
			|||
        4. Other non-masters: List[Pinter] | 
				
			|||
 | 
				
			|||
    Raises: | 
				
			|||
        SystemError: If creation of touch pointer failed | 
				
			|||
 | 
				
			|||
    Returns: | 
				
			|||
 | 
				
			|||
    """ | 
				
			|||
    v_core = None | 
				
			|||
    touch = None | 
				
			|||
    e_galax = None | 
				
			|||
    pointers = [] | 
				
			|||
    # filter pointers | 
				
			|||
    for pointer in get_pointer_iter(): | 
				
			|||
        if pointer.name == "Virtual core pointer": | 
				
			|||
            v_core = pointer | 
				
			|||
        elif "eGalax" in pointer.name: | 
				
			|||
            e_galax = pointer | 
				
			|||
        elif pointer.name == "touch pointer": | 
				
			|||
            touch = pointer | 
				
			|||
        elif not pointer.master: | 
				
			|||
            pointers.append(pointer) | 
				
			|||
 | 
				
			|||
    if not touch: | 
				
			|||
        touch = create_touch_master() | 
				
			|||
 | 
				
			|||
    return v_core, touch, e_galax, pointers | 
				
			|||
@ -1,113 +0,0 @@ | 
				
			|||
"""RandR | 
				
			|||
 | 
				
			|||
Author: Ali Hatami Tajik [hatam](mailto:a.hatam008@gmail.com) | 
				
			|||
Creation Date:   2023 Mar 04 | 
				
			|||
 | 
				
			|||
--- | 
				
			|||
This module provides a wrapper utility around xrandr. | 
				
			|||
 | 
				
			|||
Classes: | 
				
			|||
    Mode | 
				
			|||
    Setting | 
				
			|||
    Screen | 
				
			|||
    Dir | 
				
			|||
    Pos | 
				
			|||
 | 
				
			|||
 | 
				
			|||
Utilities: | 
				
			|||
    get_screens | 
				
			|||
     | 
				
			|||
""" | 
				
			|||
 | 
				
			|||
from enum import Enum | 
				
			|||
from dataclasses import dataclass | 
				
			|||
from typing import List | 
				
			|||
 | 
				
			|||
from Xlib.ext import randr as rnd | 
				
			|||
 | 
				
			|||
 | 
				
			|||
# TODO: Option class which can be applied by get_args method | 
				
			|||
# TODO: Screen-related option class ~ | 
				
			|||
# TODO: abs position | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class Pos(Enum): | 
				
			|||
    """Position types in xrandr | 
				
			|||
 | 
				
			|||
    Position the output relative to the position of another output. | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    LEFT_OF = (0,) | 
				
			|||
    RIGHT_OF = (1,) | 
				
			|||
    ABOVE = (2,) | 
				
			|||
    BELOW = (3,) | 
				
			|||
    SAME_AS = 4 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class RotationDir(Enum): | 
				
			|||
    """Rotation direction | 
				
			|||
 | 
				
			|||
    This causes the output contents to be rotated in the specified direction. | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    NORMAL = (0,) | 
				
			|||
    LEFT = (1,) | 
				
			|||
    RIGHT = (2,) | 
				
			|||
    INVERTED = 3 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class ReflectDir(Enum): | 
				
			|||
    """Reflection direction | 
				
			|||
 | 
				
			|||
    This causes the output contents to be reflected across the specified axes. | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    NORMAL = (0,) | 
				
			|||
    X = (1,) | 
				
			|||
    Y = (2,) | 
				
			|||
    XY = 3 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
@dataclass | 
				
			|||
class Setting: | 
				
			|||
    """Settings of a screen | 
				
			|||
 | 
				
			|||
    This data struct will be used as the config of each screen. Note that | 
				
			|||
    default screen cannot be use | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    resolution = (None,) | 
				
			|||
    is_primary = (False,) | 
				
			|||
    is_enabeled = (True,) | 
				
			|||
    rotation = None | 
				
			|||
    position = None | 
				
			|||
    reflection = None | 
				
			|||
 | 
				
			|||
 | 
				
			|||
@dataclass | 
				
			|||
class Mode: | 
				
			|||
    """Mode | 
				
			|||
 | 
				
			|||
    Mode of the screen including width, height, refresh rate(s) | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
    height: int = 0 | 
				
			|||
    width: int = 0 | 
				
			|||
    frequency: List[int] = [] | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class Screen: | 
				
			|||
    """Screen class | 
				
			|||
 | 
				
			|||
    This class will hold screen properties and methods related to the screens. | 
				
			|||
 | 
				
			|||
    At the time it will use xrandr (and not the verbose mode) to list the | 
				
			|||
    screens and modes. | 
				
			|||
    """ | 
				
			|||
 | 
				
			|||
 | 
				
			|||
class Monitor: | 
				
			|||
    """Monitor Class | 
				
			|||
 | 
				
			|||
    List Monitor Outputs and their states | 
				
			|||
    """ | 
				
			|||
@ -1,106 +0,0 @@ | 
				
			|||
import subprocess | 
				
			|||
from pathlib import Path | 
				
			|||
import os | 
				
			|||
 | 
				
			|||
ENCODING = "utf-8" | 
				
			|||
XINPUT = "/ust/bin/xinput" | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def exec_xinput(args: list): | 
				
			|||
    args.insert(0, XINPUT) | 
				
			|||
    _read, _write = os.pipe() | 
				
			|||
    write_fd = os.fdopen(_write, "w", 0) | 
				
			|||
    os.read() | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_list_short(): | 
				
			|||
    """Returns string output of the `xinput --list --short` command encoded as | 
				
			|||
    UTF-8""" | 
				
			|||
    completed = subprocess.run( | 
				
			|||
        [XINPUT, "--list", "--short"], capture_output=True | 
				
			|||
    ) | 
				
			|||
    return completed.stdout.decode(ENCODING) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_list_short_with(id): | 
				
			|||
    """Short List of the id | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        id (int): id registered in xinput | 
				
			|||
 | 
				
			|||
    Rises: | 
				
			|||
        ValueError: in case of id not found in devices | 
				
			|||
    """ | 
				
			|||
    completed = subprocess.run( | 
				
			|||
        [XINPUT, "--list", "--short", str(id)], capture_output=True | 
				
			|||
    ) | 
				
			|||
 | 
				
			|||
    if completed.returncode == 0: | 
				
			|||
        return completed.stdout.decode(ENCODING) | 
				
			|||
    else: | 
				
			|||
        ValueError(f"id[{id}] is not registered") | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def reattach(id, master): | 
				
			|||
    """Reattach a device to a master | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        id (str|int): name of the slave or id | 
				
			|||
        master (_type_): _description_ | 
				
			|||
 | 
				
			|||
    TODO: Error handling should be done. BUT, if the master is not a master or | 
				
			|||
    id is not valid, xinput will not do anything and nothing bad will happen :) | 
				
			|||
    """ | 
				
			|||
    completed = subprocess.run( | 
				
			|||
        [XINPUT, "--reattach", str(id), str(master)], capture_output=True | 
				
			|||
    ) | 
				
			|||
 | 
				
			|||
    return completed.returncode | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_ids(): | 
				
			|||
    """returns list of ids registered in xinput""" | 
				
			|||
    completed = subprocess.run( | 
				
			|||
        [XINPUT, "--list", "--id-only"], capture_output=True | 
				
			|||
    ) | 
				
			|||
    return list(map(int, completed.stdout.decode(ENCODING).split())) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def create_master(name: str = "touch"): | 
				
			|||
    """Creates master with specified name | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        name (str, optional): name of the master. Defaults to 'touch'. | 
				
			|||
    """ | 
				
			|||
    completed = subprocess.run([XINPUT, "create-master", name]) | 
				
			|||
 | 
				
			|||
    return completed.returncode | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_xi_id_by_name(name): | 
				
			|||
    """find device id from name | 
				
			|||
 | 
				
			|||
    Args: | 
				
			|||
        name (str): name of the device | 
				
			|||
    """ | 
				
			|||
    completed = subprocess.run( | 
				
			|||
        [XINPUT, "list", "--id-only", name], capture_output=True | 
				
			|||
    ) | 
				
			|||
 | 
				
			|||
    if completed.returncode == 1: | 
				
			|||
        return None | 
				
			|||
    else: | 
				
			|||
        return int(completed.stdout.decode(ENCODING)) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def map_to_output(output, device_id): | 
				
			|||
    # TODO | 
				
			|||
    pass | 
				
			|||
 | 
				
			|||
 | 
				
			|||
def get_edid_dev_path(): | 
				
			|||
    """returns iterator of pathes of devices with edid | 
				
			|||
 | 
				
			|||
    devices which has EDID are monitors. | 
				
			|||
    """ | 
				
			|||
    return Path("/sys/devices").rglob("edid") | 
				
			|||
					Loading…
					
					
				
		Reference in new issue