4
\$\begingroup\$

My today's goal was to create a little advanced mouse clicker in Bash for Linux powered by xdotool. This clicker implements 15 pixel random range in which it clicks for some users not wanting to get caught for some cheating. But that is not the idea. It is just part of it. It may have multiple uses.

There may be multiple issues. Although I tried hard over night to get "through" Bash. I am no question an expert on Bash, I am trying to learn Bash.

Further, this code is free to anyone.

Please address the potential issues.

#!/bin/bash


function random_number {

    # we take two parameters: from and to, inclusive
    # we return random number within this range

    echo $[ $1 + $[ RANDOM % $2 ]]

}


function activate_window_via_name {

    xdotool search --name "$1" windowactivate --sync

}


# we need to keep track of these two variables used by mouse_click function

previous_rand=10
operation_add=true


function mouse_click {

    # 1. invert the operation_add boolean value;
    #    it seems Bash does not have inbuilt command for that
    # 2. operation_add determines whether we will be adding or
    #    subtracting the random number later

    if [[ $operation_add == true ]]; then

        operation_add=false

    else

        operation_add=true

    fi

    # 2. generate random number between 0 and 7, inclusive;
    #    if the generated number is the same as the previous_rand,
    #    generate until it is different
    # 2. rand will be later used as pixel offset from the given coordinates

    rand=$(random_number 0 7)

    while [[ $rand == $previous_rand ]]; do

        rand=$(random_number 0 7)

    done

    # 3. we don't want to repeat clicks right with the same offset,
    #    so we store information about the previous_rand here

    previous_rand=$rand

    # WHAT IS THIS, WHY? probably was just temporary

    pos_x=$1
    pos_y=$2

    # 4. depending on the boolean value of operation_add,
    #    we either add the rand, or subtract it to/from the position x, y

    if [[ $operation_add == true ]]; then

        pos_x=$(($1 + $rand))
        pos_y=$(($2 + $rand))

    else

        pos_x=$(($1 - $rand))
        pos_y=$(($2 - $rand))

    fi

    # ------------------------------------------

    # 1. activate the window in which we want to get the job done and wait for sync,
    #    we need to do this before each click,
    #    because the user may have clicked on some other window during the 2 second delay
    # 2. move the mouse cursor to the given position and wait for sync
    # 3. click the left mouse button
    # 4. restore the original mouse cursor position and wait for sync
    # 5. wait for 2 seconds

    activate_window_via_name    "the window name"

    xdotool     mousemove --sync $pos_x $pos_y \
                click 1 \
                mousemove --sync restore \
                sleep 2

}


function mouse_click_coords {

    # accept one parameter as an array (of even number of integers)

    coords="${!1}"

    # iterate through the whole array and do mouse clicks on the stored positions

    for (( i = 0; i < "${#coords[*]}"; i = i + 2 ))
    do

        mouse_click     ${coords[$i]}   ${coords[$i + 1]}

    done

}


function do_the_job_on_1920x1080 {

    # these are obviously not the real coordinates, but just a sample

    local coords=(
        1000    500
        1000    500
        1000    500
        1000    500
        1000    500
    )

    mouse_click_coords  coords[@]

}


function do_the_job_on_3840x1080 {

    # these are obviously not the real coordinates, but just a sample

    local coords=(
        3000    500
        3000    500
        3000    500
        3000    500
        3000    500
    )

    mouse_click_coords  coords[@]

}


function get_screen_resolution_x {

    # we get the overall resolution in x axis
    # there is no need for echo as it will print by itself

    xdpyinfo | awk -F '[ x]+' '/dimensions:/{print $3}'

}


function get_screen_resolution_y {

    # we get the overall resolution in y axis
    # there is no need for echo as it will print by itself

    xdpyinfo | awk -F '[ x]+' '/dimensions:/{print $4}'

}


function do_some_mouse_clicking_job_until_ctrlc {

    local resolution_x=$(get_screen_resolution_x)
    local resolution_y=$(get_screen_resolution_y)

    if [ $resolution_x == 1920 ] && [ $resolution_y == 1080 ]; then
        do_the_job_on_1920x1080
    fi

    if [ $resolution_x == 3840 ] && [ $resolution_y == 1080 ]; then
        do_the_job_on_3840x1080
    fi

}


while true
do

    do_some_mouse_clicking_job_until_ctrlc

    # WAIT FOR 10 MINUTES

    sleep 600

done
\$\endgroup\$
5
\$\begingroup\$

Use $((...)) instead of $[...]

The $[...] syntax is obsolete. Use $((...)) instead.

Also, you don't need to use $ for variables within $((...)). So instead of this:

echo $[ $1 + $[ RANDOM % $2 ]]

You can write like this:

echo $(($1 + RANDOM % $2))

Quote the right-hand side of == in [[ ... ]]

Instead of this:

while [[ $rand == $previous_rand ]]; do

Write like this, to prevent glob matching:

while [[ $rand == "$previous_rand" ]]; do

Remove unnecessary code

These lines are unnecessary and can be safely removed:

# WHAT IS THIS, WHY? probably was just temporary

pos_x=$1
pos_y=$2

Double-quote to prevent globbing and word splitting

In this call:

    mouse_click     ${coords[$i]}   ${coords[$i + 1]}

You should double-quote both arguments to prevent globbing and word splitting.

Passing arrays to functions

The way you pass an array to mouse_click_coords is a bit unusual, and can be confusing. I think it will be cleaner and simpler to pass the values instead:

mouse_click_coords() {
    local coords=("$@")
    for ((i = 0; i < ${#coords[@]}; i += 2)); do
        mouse_click "${coords[$i]}" "${coords[$i + 1]}"
    done
}

function do_the_job_on_1920x1080 {
    local coords=(
        1000    500
        1000    500
        1000    500
        1000    500
        1000    500
    )

    mouse_click_coords "${coords[@]}"
}

Simplify do_some_mouse_clicking_job_until_ctrlc

Instead of getting the x and y resolutions separately with get_screen_resolution_x and get_screen_resolution_y, it will be simpler to get them together:

do_some_mouse_clicking_job_until_ctrlc() {
    local resolution=$(xdpyinfo | awk '/dimensions:/ {print $2}')

    case "$resolution" in
        1920x1080) do_the_job_on_1920x1080 ;;
        3840x1080) do_the_job_on_3840x1080 ;;
    esac
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.