1

I am trying to code a BASH script that will do the following while a program's window has focus. This would be done under KDE:

While holding left mouse button:

  1. press alt-4 key
  2. check for color (white) at pixel position 1; if pixel color exists, press alt-1 key
  3. check for color (white) at pixel position 2; if pixel color exists, press alt-2 key
  4. check for color (white) at pixel position 3; if pixel color exists, press alt-3 key

Single press mouse button 4:

  1. toggle script sequence on/off
  2. press alt-5

I have the following code but it seems to be quite slow. Any way to optimize?

#!/bin/bash

# mouse id.  use xinput --list.  verify with xinput --query-state [id]
mouse=12

# set colors with xwd.  run the following in terminal to get cursor position:
# while true; do xdotool getmouselocation; sleep 0.2; clear; done

while :; do
    state="$(xinput --query-state "$mouse")"
    color1="$(xwd -root -silent | convert xwd:- -depth 8 -crop "1x1+195+247" txt:- | grep -om1 '#\w\+')"
    color2="$(xwd -root -silent | convert xwd:- -depth 8 -crop "1x1+1407+681" txt:- | grep -om1 '#\w\+')"
    color3="$(xwd -root -silent | convert xwd:- -depth 8 -crop "1x1+1200+256" txt:- | grep -om1 '#\w\+')"
    color4="$(xwd -root -silent | convert xwd:- -depth 8 -crop "1x1+1095+257" txt:- | grep -om1 '#\w\+')"
    color5="$(xwd -root -silent | convert xwd:- -depth 8 -crop "1x1+1195+258" txt:- | grep -om1 '#\w\+')"


    # if lmb (mouse 1) pressed
    if [[ "$state" == *"button[1]=down"* ]]; then
        if [[ "$color1" == "#FFFFFF" ]]; then
            xdotool key --clearmodifiers a
        elif [[ "$color2" == *"#FFFFFF"* ]]; then
            xdotool key --clearmodifiers b
        elif [[ "$color3" == *"#FFFFFF"* ]]; then
            xdotool key --clearmodifiers c
        elif [[ "$color4" == *"#FFFFFF"* ]]; then
            xdotool key --clearmodifiers d
        elif [[ "$color5" == *"#FFFFFF"* ]]; then
            xdotool key --clearmodifiers e
        fi
    fi
done

Any help would be appreciated

proxyx
  • 13
  • 4

1 Answers1

1

A couple of things spring to mind...

Firstly, it seems you are not really interested in the colours of the pixels if the left mouse button is not pressed, so I would avoid getting all the colours if that is not the case. I mean:

while :; do

    state="$(xinput --query-state "$mouse")"

    # Don't do all the xwd | convert | grep stuff here
 
    # if lmb (mouse 1) pressed
    if [[ "$state" == *"button[1]=down"* ]]; then

       # Do xwd | convert | grep stuff here

    fi
done

That should allow you to test more frequently.


Secondly, you are calling xwd which starts a process and grabs megabytes of data, then starting convert which is another process that receives megabytes of data and then starting grep. And you are doing all that 5 times to get just five pixels. So, instead of that, start a single xwd, a single convert and get your 5 pixels in one go.

Rather than use xwd, I am just generating a repeatable, fixed image each time here. Your code does this:

# Make same random image 5 times and extract a single pixel each time
magick -seed 42 -size 100x100 xc: +noise random -depth 8 -crop 1x1+10+10 txt:
magick -seed 42 -size 100x100 xc: +noise random -depth 8 -crop 1x1+20+20 txt:
magick -seed 42 -size 100x100 xc: +noise random -depth 8 -crop 1x1+30+30 txt:
magick -seed 42 -size 100x100 xc: +noise random -depth 8 -crop 1x1+40+40 txt:
magick -seed 42 -size 100x100 xc: +noise random -depth 8 -crop 1x1+50+50 txt:

which produces this:

# ImageMagick pixel enumeration: 1,1,0,255,srgb
0,0: (120,134,192)  #7886C0  srgb(120,134,192)

# ImageMagick pixel enumeration: 1,1,0,255,srgb
0,0: (86,188,188)  #56BCBC  srgb(86,188,188)

# ImageMagick pixel enumeration: 1,1,0,255,srgb
0,0: (108,12,24)  #6C0C18  srgb(108,12,24)

# ImageMagick pixel enumeration: 1,1,0,255,srgb
0,0: (174,137,144)  #AE8990  srgb(174,137,144)

# ImageMagick pixel enumeration: 1,1,0,255,srgb
0,0: (44,185,31)  #2CB91F  srgb(44,185,31)

I am suggesting this:

# Make same random image, but extract 5 pixels in one fell swoop
magick -seed 42 -size 100x100 xc: +noise random -depth 8 \
   \( -clone 0 -crop 1x1+10+10 \) \
   \( -clone 0 -crop 1x1+20+20 \) \
   \( -clone 0 -crop 1x1+30+30 \) \
   \( -clone 0 -crop 1x1+40+40 \) \
   \( -clone 0 -crop 1x1+50+50 \) \
   -delete 0 -append txt:

which produces the same 5 pixels in a single 5-pixel image in just one process rather than 5 xwd processes and 5 convert processes:

# ImageMagick pixel enumeration: 1,5,0,255,srgb
0,0: (120,134,192)  #7886C0  srgb(120,134,192)
0,1: (86,188,188)  #56BCBC  srgb(86,188,188)
0,2: (108,12,24)  #6C0C18  srgb(108,12,24)
0,3: (174,137,144)  #AE8990  srgb(174,137,144)
0,4: (44,185,31)  #2CB91F  srgb(44,185,31)

You might consider piping the output from the previous line into awk along these lines:

magick -seed 42 -size 100x100 xc: +noise random -depth 8 \
   \( -clone 0 -crop 1x1+10+10 \) \
   ...
   \( -clone 0 -crop 1x1+50+50 \) \
   -delete 0 -append txt: |
   awk 'NR==1 {next} /#FFFFFF/ {print "white"; next} {print "not"}'

Then you'll get something along these lines:

not
white
white
not
not

Here is my best effort, bearing in mind I don't have X11 available to me, or even a computer for testing:

while : ; do
    # Check if mouse pressed
    state="$(xinput --query-state "$mouse")"

    if [[ "$state" == *"button[1]=down"* ]]; then
       read color1 color2 color3 color4 color5 < <( xwd -root -silent | 
         convert xwd:- -depth 8 \
            \( -clone 0 -crop 1x1+195+247  \) \
            \( -clone 0 -crop 1x1+1407+681 \) \
            \( -clone 0 -crop 1x1+1200+256 \) \
            \( -clone 0 -crop 1x1+1095+257 \) \
            \( -clone 0 -crop 1x1+1195+258 \) \
            -delete 0 -append txt: |
               awk '
                  NR==1     { out =""; next } 
                  /#FFFFFF/ { out = out "white "; next } { out = out "not " }
                  END       { print out }
               ' )

       echo $color1 $color2 $color3 $color4 $color5
    fi
done

If it has some bugs, you can debug it by running like this:

bash -xv THISSCRIPT.sh

or paste it into shellcheck.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • This is an amazing solution! Thanks for your input, Mark. One point of clarification, if you have a moment: How would I parse the awk result to issue the xdotool button press based on the result of "white"? Apologies if that is a newb question. I'm still feeling my way through this. Thanks again! – proxyx Oct 19 '22 at 14:47
  • I added my best effort without any computer available. so have a look at the end of my answer for the latest attempt. – Mark Setchell Oct 19 '22 at 16:30
  • Works perfectly...and you did it on the back of a napkin! Hope others can take advantage of this...it will help me greatly for some editing work. Much appreciated, Mark! – proxyx Oct 19 '22 at 18:10
  • Cool - good luck with your project. Come back and ask a new question if you get stuck again - questions are free - and answers! – Mark Setchell Oct 19 '22 at 18:13