23 changed files with 11 additions and 1626 deletions
			
			
		@ -1,98 +1,20 @@ | 
				
			|||||
#!/bin/env bash | 
					#!/bin/env bash | 
				
			||||
 | 
					
 | 
				
			||||
source ./src/scripts/utils/source.sh | 
					sudo cp -r src/rules/* /etc/udev/rules.d | 
				
			||||
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 udevadm control --reload-rules  | 
					sudo udevadm control --reload-rules  | 
				
			||||
sudo udevadm trigger | 
					sudo udevadm trigger | 
				
			||||
 | 
					
 | 
				
			||||
draw_progress_bar 90 | 
					 | 
				
			||||
sudo cp src/scripts/utils/usb/usb-storage-action /usr/local/bin/ | 
					sudo cp src/scripts/utils/usb/usb-storage-action /usr/local/bin/ | 
				
			||||
sudo chmod +x /usr/local/bin/usb-storage-action | 
					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-socket-sono.socket /etc/systemd/system | 
					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 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.socket | 
				
			||||
sudo systemctl enable usb-socket-sono.service | 
					 | 
				
			||||
sudo systemctl start usb-socket-sono.socket | 
					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 cp src/scripts/utils/dvd/dvd-storage-action /usr/local/bin/ | 
				
			||||
sudo chmod +x /usr/local/bin/dvd-storage-action | 
					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-socket-sono.socket /etc/systemd/system | 
					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 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.socket | 
				
			||||
sudo systemctl enable dvd-socket-sono.service | 
					 | 
				
			||||
sudo systemctl start dvd-socket-sono.socket | 
					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,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] | 
					[Unit] | 
				
			||||
Description=Example Unix Socket | 
					Description=Socket for DVD events (Sono) | 
				
			||||
 | 
					Requires=dvd-socket-sono.service | 
				
			||||
 | 
					
 | 
				
			||||
[Socket] | 
					[Socket] | 
				
			||||
ListenStream=/tmp/dvd-Sono-Socket.socket | 
					ListenStream=/run/dvd-Sono-Socket.socket | 
				
			||||
Accept=false | 
					 | 
				
			||||
 | 
					
 | 
				
			||||
[Install] | 
					[Install] | 
				
			||||
WantedBy=sockets.target | 
					WantedBy=sockets.target | 
				
			||||
 | 
				
			|||||
@ -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] | 
					[Unit] | 
				
			||||
Description=Example Unix Socket | 
					Description=Socket for USB events (Sono) | 
				
			||||
 | 
					Requires=usb-socket-sono.service | 
				
			||||
 | 
					
 | 
				
			||||
[Socket] | 
					[Socket] | 
				
			||||
ListenStream=/tmp/usb-Sono-Socket.socket | 
					ListenStream=/run/usb-Sono-Socket.socket | 
				
			||||
Accept=false | 
					 | 
				
			||||
 | 
					
 | 
				
			||||
[Install] | 
					[Install] | 
				
			||||
WantedBy=sockets.target | 
					WantedBy=sockets.target | 
				
			||||
 | 
				
			|||||
@ -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