You are not logged in.

#1 2015-10-19 06:37:50

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Trouble piping xev output - wm independent desktop menu [solved]

Greetings!

After long searching I finally discovered utility to monitor mouse events on rootwindow.

Problem is, I cannot pipe or redirect this:

xev -root -event button | awk '/button/{print $4}'

This outputs "1," "2," and so on depending on what mouse button waas pressed. So I tried to pipe it into a script to lauch menu when desktop is clicked:

#!/bin/bash
xev -root -event button | awk '/button/{print $4}' | while IFS=: read button; do
    case $button in
        # occurs on clicking desktop
        1,) dboxmenu ;;
        2,) BspwmWindowMenu ;;
        3,) BspwmDesktopMenu ;;
        4,) bspc desktop -f next ;;
        5,) bspc desktop -f prev ;;
    esac
done

It was modeled after this script, which is known to work:

#!/bin/sh
#
# z3bra - 2014 (c) wtfpl
# focus a window when it is created
# depends on: wew focus.sh

wew | while IFS=: read ev wid; do
    case $ev in
        # occurs on mapping requests
        19) wattr o $wid || focus.sh $wid ;;
    esac
done

It does not work however. I suspect that the problem is with the pipe, because while this works

xev -root -event button | awk '/button/{print $4}'

and this works:

echo "1," | cut -c1

This outputs nothing:

xev -root -event button | awk '/button/{print $4}' | cut -c1

Any ideas what might cause my pipe to fail?

Last edited by Chrysostomus (2015-10-19 14:07:01)


The difference between reality and fiction is that fiction has to make sense.

Offline

#2 2015-10-19 06:45:45

jasonwryan
Anarchist
From: .nz
Registered: 2009-05-09
Posts: 30,424
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

No idea, but you can hack this with:

{print +$4}

Arch + dwm   •   Mercurial repos  •   Surfraw

Registered Linux User #482438

Offline

#3 2015-10-19 06:46:40

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Also, if somenone knowing awk can advice me how to shave off the unnecessary "," in output without piping things further, I would appreciate it. Hoever, if it is awk that prevents piping,I propably need to use sed instead.


The difference between reality and fiction is that fiction has to make sense.

Offline

#4 2015-10-19 06:48:11

jasonwryan
Anarchist
From: .nz
Registered: 2009-05-09
Posts: 30,424
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

jasonwryan wrote:

No idea, but you can hack this with:

{print +$4}

Or, with more bloat:

{sub(/,/,""); print $4}

Arch + dwm   •   Mercurial repos  •   Surfraw

Registered Linux User #482438

Offline

#5 2015-10-19 06:49:53

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Thanks! Useful for the future.


The difference between reality and fiction is that fiction has to make sense.

Offline

#6 2015-10-19 10:49:56

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Okay, I tried replacing awk with grep:

#!/bin/sh
xev -root -event button |  grep -o --color=never 'button..' | while IFS=: read button; do
    case $button in
        # occurs on clicking desktop
        "button 1") dboxmenu ;;
        "button 2") BspwmWindowMenu ;;
        "button 3") BspwmDesktopMenu ;;
        "button 4") bspc desktop -f next ;;
        "button 5") bspc desktop -f prev ;;
    esac
done

Does not change things. Problem is therefore not specific to awk.


The difference between reality and fiction is that fiction has to make sense.

Offline

#7 2015-10-19 11:25:35

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,444
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Why are you setting the IFS to a colon?  Where is the colon comming from?  Just get rid of that as the values are separated by newlines which is the default IFS.


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#8 2015-10-19 11:26:50

Raynman
Member
Registered: 2011-10-22
Posts: 1,539

Re: Trouble piping xev output - wm independent desktop menu [solved]

It's a buffering problem. With grep, use the --line-buffered option. With awk, call fflush() after print.

Offline

#9 2015-10-19 11:39:40

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Thanks for the ideas!
Colon was from original script using wew, and I had no idea what IFS was.

Buffering problem would explain the issue, as I read of similar problems with xev. I had no idea how to solve it though. I'll try if this works.

Many thanks to you both!


The difference between reality and fiction is that fiction has to make sense.

Offline

#10 2015-10-19 12:27:05

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,444
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

The buffering would only be a problem with an IFS other than a newline.  But just get rid of the IFS - and don't try to reuse other peoples scripts without knowing anything about what they are doing.

In this case it only led to a lack of the intended result - but in other cases you could end up with unindended results that remove or overwrite valuable files.


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#11 2015-10-19 13:20:12

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

I appreciate the warning, although I think I was quite safe in this particular case considering IFS was the only thing I did not understand about my scripts syntax. I have used case and read statements before, and I knew what output xev was going to give, so I knew nothing dangerous was going to happen. But had I not understood that, modifying the script would not have been safe.

My current script is this:

#!/bin/sh
xev -root -event button | grep --line-buffered '0x100,' | grep -o --line-buffered --color=never 'button..' | while read button; do
    case $button in
        # occurs on clicking desktop
        "button 1") dboxmenu ;;
        "button 2") BspwmWindowMenu ;;
        "button 3") BspwmDesktopMenu ;;
        "button 4") bspc desktop -f next ;;
        "button 5") bspc desktop -f prev ;;
    esac
done

I needed the second grep to limit actions to keyrelease events only instead of including both keypress and keyrelease events.

Thank you very much for all your help!

Last edited by Chrysostomus (2015-10-19 13:23:43)


The difference between reality and fiction is that fiction has to make sense.

Offline

#12 2015-10-19 13:24:42

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

I wrote too soon. This limits output to only button 1. I need other method to weed out keypress events.


The difference between reality and fiction is that fiction has to make sense.

Offline

#13 2015-10-19 13:38:24

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

This works as intented. I only wish I could do it with single grep/awk to save a process.

#!/bin/sh
xev -root -event button | grep --line-buffered -A 3 'ButtonRelease' | grep -o --line-buffered --color=never 'button..' | while read button; do
    case $button in
        # occurs on clicking desktop
        "button 1") dboxmenu ;;
        "button 2") BspwmWindowMenu ;;
        "button 3") BspwmDesktopMenu ;;
        "button 4") bspc desktop -f next ;;
        "button 5") bspc desktop -f prev ;;
    esac
done

The difference between reality and fiction is that fiction has to make sense.

Offline

#14 2015-10-19 14:24:03

Raynman
Member
Registered: 2011-10-22
Posts: 1,539

Re: Trouble piping xev output - wm independent desktop menu [solved]

Trilby wrote:

The buffering would only be a problem with an IFS other than a newline.

I had actually forgotten about the strange IFS by the time I'd figured out the buffering thing, because I tested simply with cat instead of a while/read loop. An output buffer of more than a line is problematic regardless, with one line per button event.

Chrysostomus wrote:

This works as intented. I only wish I could do it with single grep/awk to save a process.

xev -root -event button | awk -F', ' '/ButtonRelease/{buttonNR=NR+2}; NR==buttonNR {print $2;fflush()}' | while read ...

Offline

#15 2015-10-19 14:47:09

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,444
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Or the other awk approach:

... | awk '/^ButtonRelease/ { getline; getline; print +$4; fflush(); }' | ...

You could also put the case in awk to avoid the extra process and the need for flushing the output:

... | awk '/^ButtonRelease/ { getline; getline; switch($4) {
   case "1,": system("command to run for button 1"); break;
   case "2,": system("command to run for button 2"); break;
}'

(I'm tempted to write a window manager in awk, just for the hell of it)

EDIT: forget all that getline crap - I don't know why this didn't occur to me earlier:

... | awk 'BEGIN { RS=""; } /ButtonPress/ { print +$20; fflush(); }' | while ...

"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#16 2015-10-19 15:22:57

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Whoa, that looks sweet... I'll see if I can implement it, including case in awk is genious. Only throuble is that I source the command options from another file as bash variables, so I need to make them awk variables... Here is my current script before awkification:

#!/bin/bash
#Make sure only one instance of rootmenu is run
if ! mkdir /tmp/rootmenu.lock; then
    printf "Failed to aquire lock.\n" >&2
    exit 1
fi
trap 'rm -rf /tmp/rootmenu.lock' EXIT  # remove the lockdir on exit

#Setup and source configurationfile
if ! [ -f "$HOME/.rootmenurc" ]; then
	cat > $HOME/.rootmenurc <<EOL
button1action="dboxmenu"
button2action="BspwmWindowMenu"
button3action="BspwmDesktopMenu"
button4action="bspc desktop -f next"
button5action="bspc desktop -f prev"
EOL
fi
. $HOME/.rootmenurc

#monitor events with xev and act accordingly
xev -root -event button | grep --line-buffered -A 3 'ButtonRelease' | grep -o --line-buffered --color=never 'button..' | while read button; do
    case $button in
        # occurs on clicking desktop
        "button 1") $button1action ;;
        "button 2") $button2action ;;
        "button 3") $button3action ;;
        "button 4") $button4action ;;
        "button 5") $button5action ;;
    esac
done

The difference between reality and fiction is that fiction has to make sense.

Offline

#17 2015-10-19 15:28:36

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,444
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

no need for awk variables:

   case "1,": system("' $button1action '"); break;

"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#18 2015-10-19 15:34:21

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

I tried

... | awk 'BEGIN { RS=""; } /ButtonPress/ { print +$20; fflush(); }' | while ...

And it works beautifully. Now just need to move case statement to awk...


The difference between reality and fiction is that fiction has to make sense.

Offline

#19 2015-10-19 15:46:40

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

... I really wish I actually knew awk.

I tired this:

#!/bin/bash
#Setup and source configurationfile
if ! [ -f "$HOME/.rootmenurc" ]; then
	cat > $HOME/.rootmenurc <<EOL
button1action="dboxmenu"
button2action="BspwmWindowMenu"
button3action="BspwmDesktopMenu"
button4action="bspc desktop -f next"
button5action="bspc desktop -f prev"
EOL
fi
. $HOME/.rootmenurc

#monitor events with xev and act accordingly
xev -root -event button | awk 'BEGIN { RS=""; } /ButtonPress/ switch($20) {
	 case "1": system("' $button1action '"); break;
	 case "2": system("' $button2action '"); break;
	 case "3": system("' $button3action '"); break;
	 case "4": system("' $button4action '"); break;
	 case "5": system("' $button5action '"); break;
}'
done

And got syntax errors:

awk: cmd. line:1: BEGIN { RS=""; } /ButtonPress/ switch($20) {
awk: cmd. line:1:                                ^ syntax error
awk: cmd. line:2: 	 case "1": system("
awk: cmd. line:2: 	 ^ syntax error
awk: cmd. line:2: 	 case "1": system("
awk: cmd. line:2: 	                  ^ unterminated string

I need to look deeper into this.


The difference between reality and fiction is that fiction has to make sense.

Offline

#20 2015-10-19 15:49:46

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,444
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

xev -root -event button | awk '
BEGIN { RS=""; }
/ButtonPress/ {
   switch($20) {
	 case "1,": system("' $button1action '"); break;
	 case "2,": system("' $button2action '"); break;
	 case "3,": system("' $button3action '"); break;
	 case "4,": system("' $button4action '"); break;
	 case "5,": system("' $button5action '"); break;
   }
}'

You need to add braces around everything under the ButtonPress condition.  I've also added indenting which should highlight the logical nesting: the switch block needs to be entirely within the ButtonPress block.  And using $20 rather than say +$20 you'd need the commas after the number in the case statements.


"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

#21 2015-10-19 16:01:38

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Aah, it is much more clear to read now. I still have some problems in the system statement, as error claims " in  system(" to be unterminated string, which causes syntax error.

#!/bin/bash
#Setup and source configurationfile
if ! [ -f "$HOME/.rootmenurc" ]; then
	cat > $HOME/.rootmenurc <<EOL
button1action="dboxmenu"
button2action="BspwmWindowMenu"
button3action="BspwmDesktopMenu"
button4action="bspc desktop -f next"
button5action="bspc desktop -f prev"
EOL
fi
. $HOME/.rootmenurc

#monitor events with xev and act accordingly
xev -root -event button | awk '
BEGIN { RS=""; }
/ButtonPress/ {
   switch(+$20) {
	 case "1": system("' $button1action '"); break;
	 case "2": system("' $button2action '"); break;
	 case "3": system("' $button3action '"); break;
	 case "4": system("' $button4action '"); break;
	 case "5": system("' $button5action '"); break;
   }
}'

The difference between reality and fiction is that fiction has to make sense.

Offline

#22 2015-10-19 16:05:26

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

It seems removing the two '  inside system statement does away with error and works otherwise, except that the bash variables are not expanded.


The difference between reality and fiction is that fiction has to make sense.

Offline

#23 2015-10-19 16:17:32

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

With awk variables I can get it to work:

#!/bin/bash
#Setup and source configurationfile
if ! [ -f "$HOME/.rootmenurc" ]; then
	cat > $HOME/.rootmenurc <<EOL
button1action="dboxmenu"
button2action="BspwmWindowMenu"
button3action="BspwmDesktopMenu"
button4action="bspc desktop -f next"
button5action="bspc desktop -f prev"
EOL
fi
. $HOME/.rootmenurc

#monitor events with xev and act accordingly
xev -root -event button | awk -v b1="$button1action" -v b2="$button2action" -v b3="$button3action" -v b4="$button4action" -v b5="$button5action" '
BEGIN { RS=""; }
/ButtonPress/ {
   switch(+$20) {
	 case "1": system(b1); break;
	 case "2": system(b2); break;
	 case "3": system(b3); break;
	 case "4": system(b4); break;
	 case "5": system(b5); break;
   }
}'

I wonder what was my error on the other approach? That looked much cleaner and was 0.1 MiB lighter in ram.


The difference between reality and fiction is that fiction has to make sense.

Offline

#24 2015-10-19 16:35:55

Chrysostomus
Member
Registered: 2015-09-13
Posts: 64
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

It's also weird that the messy grep solution was actually lighter in ram. It started 4 processes, with combined ram usage of about 1,6Mib:

180.0 KiB + 563.0 KiB = 743.0 KiB    rootmenu (2)
688.0 KiB + 213.0 KiB = 901.0 KiB    grep (2)

vs.

408.0 KiB + 142.5 KiB = 550.5 KiB    awkmenu
1.3 MiB +  88.5 KiB =   1.4 MiB    awk

The awk version reaches almost 2MiB. Still, having two processess less is probably more important than using 0.2MiB more ram...

Last edited by Chrysostomus (2015-10-19 16:36:44)


The difference between reality and fiction is that fiction has to make sense.

Offline

#25 2015-10-19 17:01:45

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,444
Website

Re: Trouble piping xev output - wm independent desktop menu [solved]

Sorry the error with the variables was my fault - as the commands include spaces, the shell variable needs to be quoted:

   case "1,": system("'"$button1action"'"); break;

So in the system command there is an opening double quote for awk, then a single quote to break out of awk to bash, then a double quote before the shell variable - then after the variable it's just the opposite with a double quote enclosing the shell variable, a single quote to get back to awk, and a double quote to close the string for awk's system function.

As for resource use, if you want it efficient, do it in C:

#include <stdlib.h>
#include <X11/Xlib.h>

const char *cmd[] = {
        [1] = "echo button one",
        [2] = "echo button two",
        [3] = "echo button three",
};

int main() {
        Display *dpy = XOpenDisplay(0x0);
        XSelectInput(dpy, DefaultRootWindow(dpy), ButtonPressMask);
        XEvent ev;
        while (!XNextEvent(dpy,&ev))
                if (cmd[ev.xbutton.button]) system(cmd[ev.xbutton.button]);
}

"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" -  Richard Stallman

Offline

Board footer

Powered by FluxBB