239 lines
7.0 KiB
Python
239 lines
7.0 KiB
Python
"""Auto clicker built around xdotool.
|
|
|
|
If window-name and window-id isn't given
|
|
you will be asked to select the window to send clicks to using your mouse.
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import subprocess
|
|
import time
|
|
|
|
CLICK_NAMES = {"1": "left click", "2": "middle click", "3": "right click"}
|
|
|
|
|
|
def setup_arguments():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"-t",
|
|
"--type",
|
|
choices=["1", "2", "3"],
|
|
default="1",
|
|
help="""The type of click to send. 1 is left click,
|
|
2 is middle click and 3 is right click.""",
|
|
)
|
|
parser.add_argument(
|
|
"-i",
|
|
"--interval",
|
|
type=int,
|
|
default=1000,
|
|
help="The interval between clicks in ms.",
|
|
)
|
|
parser.add_argument(
|
|
"-w",
|
|
"--window-name",
|
|
help="""The name of the window to send clicks to. A search will be made using
|
|
the provided text and a selection screen shown if there are
|
|
multiple choices.""",
|
|
)
|
|
parser.add_argument(
|
|
"-W", "--window-id", help="The id of the window to send clicks to."
|
|
)
|
|
parser.add_argument(
|
|
"-f",
|
|
"--ignore-when-focused",
|
|
action="store_true",
|
|
help="Don't send clicks to the select window when that window is focused.",
|
|
)
|
|
parser.add_argument(
|
|
"-a",
|
|
"--alternate",
|
|
choices=["1", "2", "3"],
|
|
nargs=2,
|
|
help="Alternate between two different types of clicks",
|
|
)
|
|
return parser
|
|
|
|
|
|
def main():
|
|
logging.basicConfig(format="%(asctime)s - %(message)s", level=logging.WARNING)
|
|
parser = setup_arguments()
|
|
args = parser.parse_args()
|
|
|
|
logging.debug(args)
|
|
|
|
if args.window_id:
|
|
window = {
|
|
"window_id": args.window_id,
|
|
"window_name": get_window_name_from_id(args.window_id),
|
|
}
|
|
elif args.window_name:
|
|
window_id = get_window_id_from_name(args.window_name)
|
|
window = {
|
|
"window_id": window_id,
|
|
"window_name": get_window_name_from_id(window_id),
|
|
}
|
|
else:
|
|
window = select_window()
|
|
|
|
if args.alternate is None:
|
|
click = {"click_id": args.type, "click_name": CLICK_NAMES[args.type]}
|
|
|
|
do_click(window, click, args.interval, args.ignore_when_focused)
|
|
else:
|
|
clicks = [
|
|
{
|
|
"click_id": args.alternate[0],
|
|
"click_name": CLICK_NAMES[args.alternate[0]],
|
|
},
|
|
{
|
|
"click_id": args.alternate[1],
|
|
"click_name": CLICK_NAMES[args.alternate[1]],
|
|
},
|
|
]
|
|
|
|
do_clicks(window, clicks, args.interval, args.ignore_when_focused)
|
|
|
|
|
|
def select_window():
|
|
print("Please select a window")
|
|
window_id = (
|
|
subprocess.run(["xdotool", "selectwindow"], capture_output=True)
|
|
.stdout.strip()
|
|
.decode("UTF-8")
|
|
)
|
|
|
|
window_name = get_window_name_from_id(window_id)
|
|
|
|
print(f"You have selected: {window_name}")
|
|
|
|
while True:
|
|
answer = input("Is this the correct window (yes/no):")
|
|
if answer == "yes" or answer == "no":
|
|
break
|
|
else:
|
|
print("Please provide a yes or no answer.")
|
|
|
|
if answer == "yes":
|
|
return {"window_id": window_id, "window_name": window_name}
|
|
else:
|
|
select_window()
|
|
|
|
|
|
def get_window_id_from_name(window_name):
|
|
"""Get the window id from the name of a window.
|
|
|
|
If the name matches multiple windows, the user will be presented with the names
|
|
of all the matched window to pick the one they want.
|
|
"""
|
|
output = (
|
|
subprocess.run(
|
|
["xdotool", "search", "--name", window_name], capture_output=True
|
|
)
|
|
.stdout.strip()
|
|
.decode("UTF-8")
|
|
.split("\n")
|
|
)
|
|
if len(output) == 1:
|
|
return output[0]
|
|
|
|
print("Multiple windows match the name:\n")
|
|
i = 0
|
|
for window in output:
|
|
print(f" {i}. {get_window_name_from_id(window)}")
|
|
i += 1
|
|
selected_window = int(input("\nPlease pick the desired window: "))
|
|
|
|
return output[selected_window]
|
|
|
|
|
|
def get_window_name_from_id(window_id):
|
|
"""
|
|
Gets the name of of a window using the window id.
|
|
"""
|
|
return (
|
|
subprocess.run(["xdotool", "getwindowname", window_id], capture_output=True)
|
|
.stdout.strip()
|
|
.decode("UTF-8")
|
|
)
|
|
|
|
|
|
def do_click(window, click, interval, ignore_when_focused):
|
|
print(
|
|
f"Sending {click['click_name']}s every {interval}ms to {window['window_name']}"
|
|
)
|
|
|
|
interval = interval / 1000
|
|
next_time = time.time() + interval
|
|
|
|
while True:
|
|
time.sleep(max(0, next_time - time.time()))
|
|
|
|
if ignore_when_focused:
|
|
active_window = (
|
|
subprocess.run(["xdotool", "getactivewindow"], capture_output=True)
|
|
.stdout.strip()
|
|
.decode("UTF-8")
|
|
)
|
|
|
|
logging.debug(f"active window id: {active_window}")
|
|
logging.debug(f"sending clicks to window id: {window['window_id']}")
|
|
|
|
if active_window == window["window_id"]:
|
|
next_time += (time.time() - next_time) // interval * interval + interval
|
|
logging.info("click skipped")
|
|
continue
|
|
|
|
send_click(window, click)
|
|
next_time += (time.time() - next_time) // interval * interval + interval
|
|
|
|
|
|
def do_clicks(window, clicks, interval, ignore_when_focused):
|
|
print(
|
|
f"Sending {clicks[0]['click_name']}s and {clicks[1]['click_name']}s to {window['window_name']}, alternating between them every {interval}ms."
|
|
)
|
|
|
|
interval = interval / 1000
|
|
next_time = time.time() + interval
|
|
current_click = 0
|
|
|
|
while True:
|
|
time.sleep(max(0, next_time - time.time()))
|
|
|
|
if ignore_when_focused:
|
|
active_window = (
|
|
subprocess.run(["xdotool", "getactivewindow"], capture_output=True)
|
|
.stdout.strip()
|
|
.decode("UTF-8")
|
|
)
|
|
|
|
logging.debug(f"active window id: {active_window}")
|
|
logging.debug(f"sending clicks to window id: {window['window_id']}")
|
|
|
|
if active_window == window["window_id"]:
|
|
next_time += (time.time() - next_time) // interval * interval + interval
|
|
logging.info("click skipped")
|
|
continue
|
|
|
|
send_click(window, clicks[current_click])
|
|
|
|
if current_click == 0:
|
|
current_click = 1
|
|
else:
|
|
current_click = 0
|
|
|
|
next_time += (time.time() - next_time) // interval * interval + interval
|
|
|
|
|
|
def send_click(window, click):
|
|
# Send mouse down and up separately to prevent wrong mouse inputs from being
|
|
# sent when those inputs are press on the actual mouse at the same time a click
|
|
# event is sent
|
|
subprocess.run(
|
|
["xdotool", "mousedown", "--window", window["window_id"], click["click_id"]]
|
|
)
|
|
subprocess.run(
|
|
["xdotool", "mouseup", "--window", window["window_id"], click["click_id"]]
|
|
)
|
|
logging.info(f"{click['click_name']} sent")
|