Mouse click bash script not working as intended [SOLVED]

About writing shell scripts and making the most of your shell
Forum rules
Topics in this forum are automatically closed 6 months after creation.
Post Reply
User avatar
Logansfury
Level 6
Level 6
Posts: 1237
Joined: Fri Oct 27, 2023 4:08 pm
Location: Las Vegas NV, USA

Mouse click bash script not working as intended [SOLVED]

Post by Logansfury »

Hello all,

I have a routine that uses conky to place an image on the middle of my primary monitor. To close it, I wish to click on the image, so I acquired a bash script that defines a square area surrounding the image, and waits for the mouse to be clicked while inside that defined space.

Unfortunately, the script is registering clicks anywhere on primary monitor as if I had clicked on the image inside the designated area. Can anyone please tell me what's making this script ignore the set boundaries?

clickbox_any.sh

Code: Select all

#!/bin/bash

export DISPLAY=:0
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
export XDG_CURRENT_DESKTOP=Cinnamon
export XDG_RUNTIME_DIR=/run/user/$(id -u)

# Play a beep sound at max volume
paplay /usr/share/sounds/freedesktop/stereo/complete.oga

# Get the mouse ID
MOUSE_ID=$(xinput --list | grep -i -m 1 'mouse' | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')

# Define the coordinates of the clickable box
box_x=780
box_y=385
box_width=300
box_height=260

# Function to check if the mouse is inside the box
mouse_inside_box() {
    mouse_x=$(xdotool getmouselocation --shell | grep 'X' | awk -F'=' '{print $2}')
    mouse_y=$(xdotool getmouselocation --shell | grep 'Y' | awk -F'=' '{print $2}')

    if [ "$mouse_x" -ge "$box_x" ] && [ "$mouse_x" -le "$((box_x + box_width))" ] && \
       [ "$mouse_y" -ge "$box_y" ] && [ "$mouse_y" -le "$((box_y + box_height))" ]; then
        return 0  # Mouse is inside the box
    else
        return 1  # Mouse is outside the box
    fi
}

# Wait for a mouse click event within the specified box
while true; do
    # Get the current state of the mouse buttons
    STATE1=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)

    if mouse_inside_box; then
        # Wait for a change in mouse button state
        while true; do
            sleep 0.2
            STATE2=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)
            
            # Check for a change in mouse button states
            if [ "$(comm -13 <(echo "$STATE1") <(echo "$STATE2"))" != "" ]; then
                # Play a beep sound at max volume
                paplay /usr/share/sounds/freedesktop/stereo/complete.oga
                
                # Close the conky picture display
                pkill -f "conky -c /home/logansfury/.conky/Biohazzard/conkyrc"
                
                # Exit the loop after the script is executed
                break 2  # Break out of both loops
            fi
            STATE1=$STATE2
        done
    fi
    
    sleep 0.1  # Adjust the sleep duration as needed
done

The export bit at the beginning is so that this can be launched by crontab.

Thank you for reading,

Logan
Last edited by Logansfury on Thu Feb 01, 2024 3:36 pm, edited 1 time in total.
Image <-- Cick for sudo inxi --usb -Fxxxnmprz output, updated hourly!
1000
Level 6
Level 6
Posts: 1040
Joined: Wed Jul 29, 2020 2:14 am

Re: Mouse click bash script not working as intended

Post by 1000 »

The script probably works a little differently than you expect.

Try play with game

Code: Select all

#!/bin/bash

export DISPLAY=:0
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
export XDG_CURRENT_DESKTOP=Cinnamon
export XDG_RUNTIME_DIR=/run/user/$(id -u)

# Play a beep sound at max volume
paplay /usr/share/sounds/freedesktop/stereo/complete.oga

# Get the mouse ID
Count=$(xinput --list | grep -i 'mouse' | wc -l)
if [[ "$Count" == 1 ]] ;then
    MOUSE_ID=$(xinput --list | grep -i -m 1 'mouse' | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')
elif [[ "$Count" -ge 2 ]] ;then
    echo "Warnning: We have detected two or more devices with xinput, we will try to use the last one."
    MOUSE_ID=$(xinput --list | grep -i 'mouse' | tail -n1 | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')
else
    echo "Error: Mouse not detected with command: xinput --list"
    exit 1
fi


# Define the coordinates of the clickable box
box_x=780
box_y=385
box_width=300
box_height=260

# Function to check if the mouse is inside the box
mouse_inside_box() {
    mouse_x=$(xdotool getmouselocation --shell | grep 'X' | awk -F'=' '{print $2}')
    mouse_y=$(xdotool getmouselocation --shell | grep 'Y' | awk -F'=' '{print $2}')

    if [ "$mouse_x" -ge "$box_x" ] && [ "$mouse_x" -le "$((box_x + box_width))" ] && \
       [ "$mouse_y" -ge "$box_y" ] && [ "$mouse_y" -le "$((box_y + box_height))" ]; then
        return 0  # Mouse is inside the box
    else
        return 1  # Mouse is outside the box
    fi
}

# Wait for a mouse click event within the specified box
while true; do
    # Get the current state of the mouse buttons
    STATE1=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)

    if mouse_inside_box; then
        echo "You are in the box ${mouse_x}/${mouse_y}."
        
        # Wait for a change in mouse button state
        while true; do
            sleep 0.2
            STATE2=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)
            
            # Check for a change in mouse button states
            echo "compare - You need Click"
            if [ "$(comm -13 <(echo "$STATE1") <(echo "$STATE2"))" != "" ]; then
                # Play a beep sound at max volume
                paplay /usr/share/sounds/freedesktop/stereo/complete.oga
                
                # Close the conky picture display
                #pkill -f "conky -c /home/logansfury/.conky/Biohazzard/conkyrc"
                echo "  - You clicked correctly"
                
                # Exit the loop after the script is executed
                break 2  # Break out of both loops
            else
                echo "  - missed"
            fi
            STATE1=$STATE2
        done
    fi
    
    echo "This is not that area  ${mouse_x}/${mouse_y}. \
    Look ${box_x}-$((box_x + box_width))/${box_y}-$((box_y + box_height))"
    sleep 0.1  # Adjust the sleep duration as needed
done
You probably need to improve your script.
You probably want to detect the location after clicking instead before.
Or confirm two things at once ( was clicked and mouse is inside the box )
User avatar
Logansfury
Level 6
Level 6
Posts: 1237
Joined: Fri Oct 27, 2023 4:08 pm
Location: Las Vegas NV, USA

Re: Mouse click bash script not working as intended

Post by Logansfury »

Hello 1000 :)

It's morning for me and I just woke up to your post, sorry for the delay in responding.

I see you provided an edit. Thank you sincerely for taking the time to do that.

I am going to paste your work into a .sh file, chmod it and test. I'll post back with results.
Image <-- Cick for sudo inxi --usb -Fxxxnmprz output, updated hourly!
User avatar
Logansfury
Level 6
Level 6
Posts: 1237
Joined: Fri Oct 27, 2023 4:08 pm
Location: Las Vegas NV, USA

Re: Mouse click bash script not working as intended

Post by Logansfury »

1000 wrote: Thu Feb 01, 2024 10:48 am The script probably works a little differently than you expect.

Try play with game

Code: Select all

#!/bin/bash

export DISPLAY=:0
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
export XDG_CURRENT_DESKTOP=Cinnamon
export XDG_RUNTIME_DIR=/run/user/$(id -u)

# Play a beep sound at max volume
paplay /usr/share/sounds/freedesktop/stereo/complete.oga

# Get the mouse ID
Count=$(xinput --list | grep -i 'mouse' | wc -l)
if [[ "$Count" == 1 ]] ;then
    MOUSE_ID=$(xinput --list | grep -i -m 1 'mouse' | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')
elif [[ "$Count" -ge 2 ]] ;then
    echo "Warnning: We have detected two or more devices with xinput, we will try to use the last one."
    MOUSE_ID=$(xinput --list | grep -i 'mouse' | tail -n1 | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')
else
    echo "Error: Mouse not detected with command: xinput --list"
    exit 1
fi


# Define the coordinates of the clickable box
box_x=780
box_y=385
box_width=300
box_height=260

# Function to check if the mouse is inside the box
mouse_inside_box() {
    mouse_x=$(xdotool getmouselocation --shell | grep 'X' | awk -F'=' '{print $2}')
    mouse_y=$(xdotool getmouselocation --shell | grep 'Y' | awk -F'=' '{print $2}')

    if [ "$mouse_x" -ge "$box_x" ] && [ "$mouse_x" -le "$((box_x + box_width))" ] && \
       [ "$mouse_y" -ge "$box_y" ] && [ "$mouse_y" -le "$((box_y + box_height))" ]; then
        return 0  # Mouse is inside the box
    else
        return 1  # Mouse is outside the box
    fi
}

# Wait for a mouse click event within the specified box
while true; do
    # Get the current state of the mouse buttons
    STATE1=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)

    if mouse_inside_box; then
        echo "You are in the box ${mouse_x}/${mouse_y}."
        
        # Wait for a change in mouse button state
        while true; do
            sleep 0.2
            STATE2=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)
            
            # Check for a change in mouse button states
            echo "compare - You need Click"
            if [ "$(comm -13 <(echo "$STATE1") <(echo "$STATE2"))" != "" ]; then
                # Play a beep sound at max volume
                paplay /usr/share/sounds/freedesktop/stereo/complete.oga
                
                # Close the conky picture display
                #pkill -f "conky -c /home/logansfury/.conky/Biohazzard/conkyrc"
                echo "  - You clicked correctly"
                
                # Exit the loop after the script is executed
                break 2  # Break out of both loops
            else
                echo "  - missed"
            fi
            STATE1=$STATE2
        done
    fi
    
    echo "This is not that area  ${mouse_x}/${mouse_y}. \
    Look ${box_x}-$((box_x + box_width))/${box_y}-$((box_y + box_height))"
    sleep 0.1  # Adjust the sleep duration as needed
done
You probably need to improve your script.
You probably want to detect the location after clicking instead before.
Or confirm two things at once ( was clicked and mouse is inside the box )
I just had time to experiment with your script. It is doing exactly what I wanted!

Terminal launch gave an awesome status output, and launching by double click worked flawlessly!

Thank you very much for performing this edit for me. I have removed the # from the pkill statement and it's working perfectly.
Image <-- Cick for sudo inxi --usb -Fxxxnmprz output, updated hourly!
1000
Level 6
Level 6
Posts: 1040
Joined: Wed Jul 29, 2020 2:14 am

Re: Mouse click bash script not working as intended [SOLVED]

Post by 1000 »

It is doing exactly what I wanted!
Are you sure that,
1. you want to detect when the mouse passes over the square
2. then detect the mouse click wherever it is ?

Wouldn't it be better when these two conditions are met simultaneously?
User avatar
Logansfury
Level 6
Level 6
Posts: 1237
Joined: Fri Oct 27, 2023 4:08 pm
Location: Las Vegas NV, USA

Re: Mouse click bash script not working as intended [SOLVED]

Post by Logansfury »

1000 wrote: Thu Feb 01, 2024 7:29 pm
It is doing exactly what I wanted!
Are you sure that,
1. you want to detect when the mouse passes over the square
2. then detect the mouse click wherever it is ?

Wouldn't it be better when these two conditions are met simultaneously?
I'm not qualified to do such an edit, the script you worked on was not mine but came from ChatGPT3.5 AI.

But I usually adhere to the policy "If it aint broke, don't fix it". Previous scripting outputs from the bot produced scripts that accepted clicks anywhere on screen or that failed to register the mouse-click at all. Your script recognized the area I defined that outlines the onscreen graphic in a square, and only reacts to mouse clicks within this square. It's doing just what I want and seems perfect. My machine doesn't seem to mind running the script at all. Why would I want to change anything now?
Image <-- Cick for sudo inxi --usb -Fxxxnmprz output, updated hourly!
User avatar
Koentje
Level 7
Level 7
Posts: 1581
Joined: Tue Jan 04, 2022 6:23 pm
Location: Netherlands

Re: Mouse click bash script not working as intended [SOLVED]

Post by Koentje »

As i see it, when your mouse is over the box and you don't click on it, it stays in the loop that says 'you missed it'. When you go outside the box it doesn't track your mouse anymore, it just stays in the second loop.
Image
User avatar
Logansfury
Level 6
Level 6
Posts: 1237
Joined: Fri Oct 27, 2023 4:08 pm
Location: Las Vegas NV, USA

Re: Mouse click bash script not working as intended [SOLVED]

Post by Logansfury »

Koentje wrote: Thu Feb 01, 2024 7:59 pm As i see it, when your mouse is over the box and you don't click on it, it stays in the loop that says 'you missed it'. When you go outside the box it doesn't track your mouse anymore, it just stays in the second loop.
This could be a problem. Is there a way to make detection persistent until mouse-click is detected inside box, regardless of how many times mouse may pass thru box without clicking or how many times it clicks outside of the box?

This isn't of major importance, it's my trash night closing script. My crontab at 6pm monday nights launches a script that plays an .mp3 saying "It's trash night, take the garbage outside." and uses Conky to display a biohazzard sign in the middle of my main keyboard, focused above all other windows. At 6:01pm monday nights this script is then auto-launched by crontab so that when I return from taking the trash out and resume sitting at my comp, I can just click on the biohazzard image and it goes away and stops the mouseclick detection script. :)
Image <-- Cick for sudo inxi --usb -Fxxxnmprz output, updated hourly!
1000
Level 6
Level 6
Posts: 1040
Joined: Wed Jul 29, 2020 2:14 am

Re: Mouse click bash script not working as intended [SOLVED]

Post by 1000 »

and only reacts to mouse clicks within this square.
When you pass over the square, the script will detect that
then the script will wait until you click.
The script does not know that these two conditions must be met simultaneously.

Additionally, the script does not know that you want to detect clicks only on the desktop, and not in any application.

You can treat writing scripts as fun.
You have nice lines describing what they do in the script.
Additionally, I added the echo command which allows you to see what the script is doing.
You can take it apart to see what each part does.
With the help of artificial intelligence or web search engine, you can find examples and adapt them to your needs.
Honestly you don't need our help. I mean with a little time and work you can solve the problem.
If not, you can always ask your question again.

However, bellow you have a corrected script regarding click detection.
Warning: This script will consume more "CPU" hardware resources in a square / box.
This is the price for very high sensitivity of change detection.

Code: Select all

#!/bin/bash

export DISPLAY=:0
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
export XDG_CURRENT_DESKTOP=Cinnamon
export XDG_RUNTIME_DIR=/run/user/$(id -u)

# Play a beep sound at max volume
paplay /usr/share/sounds/freedesktop/stereo/complete.oga

# Get the mouse ID
Count=$(xinput --list | grep -i 'mouse' | wc -l)
if [[ "$Count" == 1 ]] ;then
    MOUSE_ID=$(xinput --list | grep -i -m 1 'mouse' | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')
elif [[ "$Count" -ge 2 ]] ;then
    echo "Warnning: We have detected two devices with xinput, we will try to use the last one."
    MOUSE_ID=$(xinput --list | grep -i 'mouse' | tail -n1 | grep -o 'id=[0-9]\+' | grep -o '[0-9]\+')
else
    echo "Error: Mouse not detected with command: xinput --list"
    exit 1
fi


# Define the coordinates of the clickable box
box_x=780
box_y=385
box_width=300
box_height=260

# Function to check if the mouse is inside the box
mouse_inside_box() {
    mouse_x=$(xdotool getmouselocation --shell | grep 'X' | awk -F'=' '{print $2}')
    mouse_y=$(xdotool getmouselocation --shell | grep 'Y' | awk -F'=' '{print $2}')

    if [ "$mouse_x" -ge "$box_x" ] && [ "$mouse_x" -le "$((box_x + box_width))" ] && \
       [ "$mouse_y" -ge "$box_y" ] && [ "$mouse_y" -le "$((box_y + box_height))" ]; then
        return 0  # Mouse is inside the box
    else
        return 1  # Mouse is outside the box
    fi
}

# Wait for a mouse click event within the specified box
while true; do
    # Get the current state of the mouse buttons
    STATE1=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)

    if mouse_inside_box; then
        echo "You are in the box ${mouse_x}/${mouse_y}."

        STATE2=$(xinput --query-state $MOUSE_ID | grep 'button\[' | sort)
        if [ "$(comm -13 <(echo "$STATE1") <(echo "$STATE2"))" != "" ]; then
            # Play a beep sound at max volume
            paplay /usr/share/sounds/freedesktop/stereo/complete.oga
            
            echo "  - You clicked in the box. Exitting."
            exit
        fi
    else
        sleep 0.2
    fi
       
    Square="${box_x}-$((box_x + box_width))/${box_y}-$((box_y + box_height))"
    echo "Location:  ${mouse_x}/${mouse_y} Square: $Square"
done

Edited

Linux / bash allows the use of many tools and methods.
This will allow you to improve the script / use other tools if you need better performance and less hardware usage.
User avatar
Logansfury
Level 6
Level 6
Posts: 1237
Joined: Fri Oct 27, 2023 4:08 pm
Location: Las Vegas NV, USA

Re: Mouse click bash script not working as intended [SOLVED]

Post by Logansfury »

I have updated to this most current script you have provided.

Thank you very much for taking the time to do the edit :)
Image <-- Cick for sudo inxi --usb -Fxxxnmprz output, updated hourly!
Post Reply

Return to “Scripts & Bash”