Fittstool - a X11 tool for binding mouse button events at the screen corners to commands

This little tool has incredible potential… especially if wrapped in scripts in something more user-friendly. I have never heard of it before.

Name comes from Fitts Law:

Available positions:

Left, TopLeft, etc, TopCenter, BottomCenter, Right, TopRight, BottomRight, etc…

Available events:

LeftButton, RightButton, MiddleButton, WheelUp, WheelDown, WheelUpOnce, WheelDownOnce, Enter, Leave

Available in AUR:

yay fittstool

I’d love to look closer into this once I find some time to experiment.

1 Like

16 years old tool… :videocassette: .

It works nice and simple config.

Quick test [TopCenter] volume level.

:bird:

update test… when i turn off second display all corners and sides seems to work. But with second display one side not reacting.

edit :
i see now., when both display on.
The second display takes the corners and side.

:bird:

Not fancy: Fittstool GUI for quick settings and testing if things are working.

Running fittstool for the first time will create a config file.
After that change config with this one.

~/.config/fittstool/fittstoolrc

#fittstoolrc template
#volume control in the top right corner:

[BottomLeft]
WheelUp=
WheelDown=
WheelUpOnce=
WheelDownOnce=
MiddleButton=
RightButton=
LeftButoon=
Enter=
Leave=

[Left]
WheelUp=amixer -D pulse set Master 5%+
WheelDown=amixer -D pulse set Master 5%-
WheelUpOnce=
WheelDownOnce=
MiddleButton=terminator
RightButton=
LeftButton=
Enter=
Leave=

[TopCenter]
WheelUp=amixer -D pulse set Master 5%+
WheelDown=amixer -D pulse set Master 5%-
WheelUpOnce=
WheelDownOnce=
MiddleButton=
RightButton=pactl set-sink-mute @DEFAULT_SINK@ toggle
LeftButton=
Enter=
Leave=

[BottomCenter]
WheelUp=
WheelDown=
WheelUpOnce=
WheelDownOnce=
MiddleButton=terminator
RightButton=
LeftButton=
Enter=
Leave=

[BottomRight]
WheelUp=
WheelDown=
WheelUpOnce=
WheelDownOnce=
MiddleButton=terminator
RightButton=
LeftButton=
Enter=
Leave=

[TopLeft]
WheelUp=
WheelDown=
WheelUpOnce=
WheelDownOnce=
MiddleButton=terminator
RightButton=
LeftButton=
Enter=
Leave=

[Right]
WheelUp=
WheelDown=
WheelUpOnce=terminator
WheelDownOnce=
MiddleButton=
RightButton=terminator
LeftButton=
Enter=
Leave=

[TopRight]
WheelUp=
WheelDown=
WheelUpOnce=terminator
WheelDownOnce=
MiddleButton=
RightButton=terminator
LeftButton=
Enter=
Leave=


./fittstool-settings.sh

#!/bin/bash

me="fittstool-settings.sh"

CONFIG_FILE="$HOME/.config/fittstool/fittstoolrc"
[ -d "$(dirname "$CONFIG_FILE")" ] || mkdir -p "$(dirname "$CONFIG_FILE")"

POSITIONS="Left|TopLeft|TopCenter|TopRight|Right|BottomRight|BottomCenter|BottomLeft"
EVENTS=("WheelUp" "WheelDown" "WheelUpOnce" "WheelDownOnce" "MiddleButton" "RightButton" "LeftButton" "Enter" "Leave")

# Step 1: Choose Position
POSITION=$(yad --title="Choose Position" --width=300 --height=400 --center \
    --list --column="Position" --separator="" \
    $(echo $POSITIONS | tr "|" " "))

[ -z "$POSITION" ] && exit 1

# Step 2: Get existing values if present
declare -A CURRENT_VALUES
if grep -q "^\[$POSITION\]" "$CONFIG_FILE"; then
    in_section=0
    while IFS= read -r line; do
        [[ "$line" =~ ^\[$POSITION\] ]] && in_section=1 && continue
        [[ "$line" =~ ^\[.*\] ]] && [[ $in_section -eq 1 ]] && break
        [[ $in_section -eq 1 && "$line" =~ = ]] && {
            key="${line%%=*}"
            val="${line#*=}"
            CURRENT_VALUES["$key"]="$val"
        }
    done < "$CONFIG_FILE"
fi

# Step 3: Show form with pre-filled fields
FIELDS=()
for EVENT in "${EVENTS[@]}"; do
    FIELDS+=(--field="$EVENT":TXT "${CURRENT_VALUES[$EVENT]}")
done

VALUES=$(yad --title="Set Commands for $POSITION" --width=600 --form "${FIELDS[@]}" --center)
[ $? -ne 0 ] && exit 1

IFS="|" read -r "${EVENTS[@]}" <<< "$VALUES"

# Step 4: Update config file
# Remove existing section if present
sed -i "/^\[$POSITION\]/,/^\[/ {/^\[.*\]/!d}" "$CONFIG_FILE"
sed -i "/^\[$POSITION\]/d" "$CONFIG_FILE"

{
    echo "[$POSITION]"
    for EVENT in "${EVENTS[@]}"; do
        eval val=\$$EVENT
        echo "$EVENT=$val"
    done
    echo
} >> "$CONFIG_FILE"

# Step 5: Reload fittstool safely
# Get all fittstool PIDs except this script's parent
for pid in $(pgrep -f fittstool); do
    if [ "$pid" != "$PPID" ] && [ "$pid" != "$$" ]; then
        kill "$pid"
    fi
done


fittstool &


# Step 6: Confirmation
yad --center --title="Done" --text="Configuration for [$POSITION] saved!\n\nfittstool reloaded." --button=OK

# restart menu
$me;

exit 0

:bird:

1 Like

I forked the repo to make some - mostly cosmetic changes.
(as my knowledge of C programming is close to absolute zero)

  • updated README
  • fix misplaced width and height (command output)
  • allow longer commands (200 chars instead 100)

Forked repo:

fittstool package is now available from Mabox repo.

yay
yay fittstool

Notes for future consideration

Things to think about…
If we decide to implement it in Mabox in a more user-friendly way.

Enter and Leave events seem perfect for showing hints to the user

Just a quick notify-send.sh example:

Enter=notify-send.sh -u critical -R /tmp/rID "TopRight" "Hello from <b>fittstool</b>"
Leave=notify-send.sh -s $(cat /tmp/rID)

Something Yad based will be better here, as yad allows controlling window (hints) position.
Hint must off course show configured actions for entered corner(area).

User readeable command descriptions can be defined in config file after #.
Like:

LeftButton=mabox-terminal  #Quick terminal (F12)

So…

mb-fittstool wrapper script

mb-fittstool start | stop | restart

Redirects fittstool command output to file, parse it and prepare hints (config file can also be parsed off course for that).

autostart

.desktop file for GUI autostart tool


If implemented, this will make other Mabox tools obsolete: mcorners (hot-corners) and released few days ago areaclick.
Also something similar must be implemented for W-A-KP_1..9 (currently areaclick actions are spawned).

1 Like

Hi @napcok,

I have been looking at fittstool.c too.

Is this the same as …

  • What i wanted to do is looking if i could change the screen dimentions.
    When there is connected more than one monitor, always use the default ‘primary’ monitor specs.

C is completly new to me too.

#include "monitor.h"     /* if you have get_primary_monitor_geometry() */
#include "options.h"     /* if init_options() is declared there */


int pw, ph, px, py;
if (get_primary_monitor_geometry(&px, &py, &pw, &ph)) {
    init_options(pw, ph);
}


// function to get primary display width and height
int get_primary_monitor_geometry(int *x, int *y, int *w, int *h) {
    FILE *fp = popen("xrandr --query | awk '/ primary / { print $3 }'", "r");
    if (!fp) return 0;

    char geom[32];
    if (fgets(geom, sizeof(geom), fp)) {
        sscanf(geom, "%dx%d+%d+%d", w, h, x, y);
        pclose(fp);
        return 1;
    }

    pclose(fp);
    return 0;
}

:bird:

No, I just fixed some bad reporting, a trivial bug. It has nothing to do with the geometry calculations in the program - which seem to be done by XCB library.

1 Like

:penguin: :+1:

Indeed that is the proper way to do it from what i read.

:bird:

Testing Fork of FITTSTOOL.

I am calling the project FITTSMON.

Areaclick turned off. (using events with fittsmon)

New feature. (working)

  • always use primairy display dimensions when multiple display’s are connected.

Todo/want to add function.

  • option to add multiple display’s with $ fittsmon DP-0 DP-1 etc.
  • for each display custom mouse events possible.
  • possible to set area click margins.

:bird: :dashing_away: to be continued…

Where it is? Link?

1 Like

Created gitlab. Forked from @napcok.

Ready for some testing on other machines…

:bird:

1 Like

Handy fittsmonrc for testing…

Change DP-0 for your display.

# Default configuration for all enabled monitors

[TopCenter]
WheelUp=amixer -D pulse set Master 5%+
WheelDown=amixer -D pulse set Master 5%-
WheelUpOnce=notify-send "Default: Wheel Up Once"
WheelDownOnce=notify-send "Default: Wheel Down Once"
MiddleButton=notify-send "Default: Middle Button"
RightButton=pactl set-sink-mute @DEFAULT_SINK@ toggle
LeftButton=notify-send "Default: Left Button"
Enter=
Leave=

[BottomLeft]
WheelUp=notify-send "Default: BottomLeft Wheel Up"
WheelDown=notify-send "Default: BottomLeft Wheel Down"
WheelUpOnce=notify-send "Default: BottomLeft Wheel Up Once"
WheelDownOnce=notify-send "Default: BottomLeft Wheel Down Once"
MiddleButton=notify-send "Default: BottomLeft Middle Button"
RightButton=notify-send "Default: BottomLeft Right Button"
LeftButton=notify-send "Default: BottomLeft Left Button"
Enter=
Leave=

[Left        ]
WheelUp=notify-send "Default: Left Wheel Up"
WheelDown=notify-send "Default: Left Wheel Down"
WheelUpOnce=notify-send "Default: Left Wheel Up Once"
WheelDownOnce=notify-send "Default: Left Wheel Down Once"
MiddleButton=notify-send "Default: Left Middle Button"
RightButton=notify-send "Default: Left Right Button"
LeftButton=notify-send "Default: Left Left Button"
Enter=
Leave=

[BottomCenter]
WheelUp=notify-send "Default: BottomCenter Wheel Up"
WheelDown=notify-send "Default: BottomCenter Wheel Down"
WheelUpOnce=notify-send "Default: BottomCenter Wheel Up Once"
WheelDownOnce=notify-send "Default: BottomCenter Wheel Down Once"
MiddleButton=notify-send "Default: BottomCenter Middle Button"
RightButton=notify-send "Default: BottomCenter Right Button"
LeftButton=notify-send "Default: BottomCenter Left Button"
Enter=
Leave=

[TopLeft]
WheelUp=notify-send "Default: TopLeft Wheel Up"
WheelDown=notify-send "Default: TopLeft Wheel Down"
WheelUpOnce=notify-send "Default: TopLeft Wheel Up Once"
WheelDownOnce=notify-send "Default: TopLeft Wheel Down Once"
MiddleButton=notify-send "Default: TopLeft Middle Button"
RightButton=notify-send "Default: TopLeft Right Button"
LeftButton=notify-send "Default: TopLeft Left Button"
Enter=
Leave=

[BottomRight]
WheelUp=notify-send "Default: BottomRight Wheel Up"
WheelDown=notify-send "Default: BottomRight Wheel Down"
WheelUpOnce=notify-send "Default: BottomRight Wheel Up Once"
WheelDownOnce=notify-send "Default: BottomRight Wheel Down Once"
MiddleButton=notify-send  "Default: BottomRight Middle Button"
RightButton=notify-send  "Default: BottomRight Right Button"
LeftButton=notify-send  "Default: BottomRight Left Button"
Enter=
Leave=

[Right]
WheelUp=notify-send "Default: Right Wheel Up"
WheelDown=notify-send "Default: Right Wheel Down"
WheelUpOnce=notify-send "Default: Right Wheel Up Once"
WheelDownOnce=notify-send "Default: Right Wheel Down Once"
MiddleButton=terminator
RightButton=notify-send "Default: Right Right Button"
LeftButton=notify-send "Default: Right Left Button"
Enter=
Leave=

[TopRight]
WheelUp=notify-send "Default: TopRight Wheel Up"
WheelDown=notify-send "Default: TopRight Wheel Down"
WheelUpOnce=notify-send "Default: TopRight Wheel Up Once"
WheelDownOnce=notify-send "Default: TopRight Wheel Down Once"
MiddleButton=terminator
RightButton=notify-send "Default: TopRight Right Button"
LeftButton=notify-send "Default: TopRight Left Button"
Enter=
Leave=

[Left]
WheelUp=amixer -D pulse set Master 5%+
WheelDown=amixer -D pulse set Master 5%-
WheelUpOnce=notify-send "Default: Left Wheel Up Once"
WheelDownOnce=notify-send "Default: Left Wheel Down Once"
MiddleButton=notify-send "Default: Left Middle Button"
RightButton=notify-send "Default: Left Right Button"
LeftButton=notify-send "Default: Left Left Button"
Enter=
Leave=

## Monitor-specific configuration for DP-0

[DP-0-TopCenter]
WheelUp=amixer -D pulse set Master 5%+
WheelDown=amixer -D pulse set Master 5%-
WheelUpOnce=notify-send "DP-0: TopCenter Wheel Up Once"
WheelDownOnce=notify-send "DP-0: TopCenter Wheel Down Once"
MiddleButton=notify-send "DP-0: TopCenter Middle Button"
RightButton=pactl set-sink-mute @DEFAULT_SINK@ toggle
LeftButton=notify-send "DP-0: TopCenter Left Button"
Enter=
Leave=

[DP-0-BottomLeft]
WheelUp=notify-send "DP-0: BottomLeft Wheel Up"
WheelDown=notify-send "DP-0: BottomLeft Wheel Down"
WheelUpOnce=notify-send "DP-0: BottomLeft Wheel Up Once"
WheelDownOnce=notify-send "DP-0: BottomLeft Wheel Down Once"
MiddleButton=notify-send "DP-0: BottomLeft Middle Button"
RightButton=notify-send "DP-0: BottomLeft Right Button"
LeftButton=notify-send "DP-0: BottomLeft Left Button"
Enter=
Leave=

[DP-0-Left   ]
WheelUp=notify-send "DP-0: Left Wheel Up"
WheelDown=notify-send "DP-0: Left Wheel Down"
WheelUpOnce=notify-send "DP-0: Left Wheel Up Once"
WheelDownOnce=notify-send "DP-0: Left Wheel Down Once"
MiddleButton=notify-send "DP-0: Left Middle Button"
RightButton=notify-send "DP-0: Left Right Button"
LeftButton=notify-send "DP-0: Left Left Button"
Enter=
Leave=

[DP-0-BottomCenter]
WheelUp=notify-send "DP-0: BottomCenter Wheel Up"
WheelDown=notify-send "DP-0: BottomCenter Wheel Down"
WheelUpOnce=skippy-xd --paging
WheelDownOnce=skippy-xd --expose
MiddleButton=skippy-xd --expose
RightButton=notify-send "DP-0: BottomCenter Right Button"
LeftButton=notify-send "DP-0: BottomCenter Left Button"
Enter=
Leave=

[DP-0-TopLeft]
WheelUp=notify-send "DP-0: TopLeft Wheel Up"
WheelDown=notify-send "DP-0: TopLeft Wheel Down"
WheelUpOnce=notify-send "DP-0: TopLeft Wheel Up Once"
WheelDownOnce=notify-send "DP-0: TopLeft Wheel Down Once"
MiddleButton=notify-send "DP-0: TopLeft Middle Button"
RightButton=notify-send "DP-0: TopLeft Right Button"
LeftButton=notify-send "DP-0: TopLeft Left Button"
Enter=
Leave=

[DP-0-BottomRight]
WheelUp=notify-send "DP-0: BottomRight Wheel Up"
WheelDown=notify-send "DP-0: BottomRight Wheel Down"
WheelUpOnce=notify-send "DP-0: BottomRight Wheel Up Once"
WheelDownOnce=notify-send "DP-0: BottomRight Wheel Down Once"
MiddleButton=notify-send  "DP-0: BottomRight Middle Button"
RightButton=notify-send  "DP-0: BottomRight Right Button"
LeftButton=notify-send  "DP-0: BottomRight Left Button"
Enter=
Leave=

[DP-0-Right]
WheelUp=notify-send "DP-0: Right Wheel Up"
WheelDown=notify-send "DP-0: Right Wheel Down"
WheelUpOnce=notify-send "DP-0: Right Wheel Up Once"
WheelDownOnce=notify-send "DP-0: Right Wheel Down Once"
MiddleButton=terminator
RightButton=notify-send "DP-0: Right Right Button"
LeftButton=notify-send "DP-0: Right Left Button"
Enter=
Leave=

[DP-0-TopRight]
WheelUp=notify-send "DP-0: TopRight Wheel Up"
WheelDown=notify-send "DP-0: TopRight Wheel Down"
WheelUpOnce=notify-send "DP-0: TopRight Wheel Up Once"
WheelDownOnce=notify-send "DP-0: TopRight Wheel Down Once"
MiddleButton=terminator
RightButton=notify-send "DP-0: TopRight Right Button"
LeftButton=notify-send "DP-0: TopRight Left Button"
Enter=
Leave=

[DP-0-Left]
WheelUp=amixer -D pulse set Master 5%+
WheelDown=amixer -D pulse set Master 5%-
WheelUpOnce=notify-send "DP-0: Left Wheel Up Once"
WheelDownOnce=notify-send "DP-0: Left Wheel Down Once"
MiddleButton=notify-send "DP-0: Left Middle Button"
RightButton=notify-send "DP-0: Left Right Button"
LeftButton=notify-send "DP-0: Left Left Button"
Enter=
Leave=

## Monitor-specific configuration for DP-X or HDMI-X

:bird:

1 Like