I was so stunned that there was not a single Mouse Jiggler GUI in Linux. Coming from Windows 11, that was one of the first things I started to look for and I found none. I installed Shell Extension like Keep Awake, Caffeine, etc. but none serve any purpose for my use case. All I needed was something that would jiggle my mouse cursor in certain interval and keep my windows alive. So I coded one for myself, took me approx. 5-6 hours to code the whole damn thing but it's well worth.
Sharing code if anyone looking for same tool. It is written in a python, first it was 3 lines code that ran from terminal but decided to give GUI look that took helluva time. Anyway, save it in .py format, you can change the title bar name to whatever name you like, run it or compile it. Good Luck.
#!/usr/bin/env python3
import tkinter as tk
from tkinter import ttk, messagebox
import subprocess
import signal
import os
import sys
import threading
import time
import re
import glob
def find_mouse_device():
"""
Find mouse device by reading input device information from /proc/bus/input/devices
and matching it with corresponding event in /dev/input/
"""
try:
# Read the input devices file
with open('/proc/bus/input/devices', 'r') as f:
content = f.read()
# Split into device blocks
devices = content.split('\n\n')
# Look for mouse devices
for device in devices:
if 'mouse' in device.lower():
# Get the event handler number
handlers = re.search(r'H: Handlers=.*event(\d+)', device)
if handlers:
event_num = handlers.group(1)
device_path = f'/dev/input/event{event_num}'
# Verify the device exists
if os.path.exists(device_path):
# Get device name for the message
name_match = re.search(r'N: Name="([^"]+)"', device)
device_name = name_match.group(1) if name_match else "Mouse device"
return device_path, device_name
return None, None
except Exception as e:
print(f"Error finding mouse device: {e}")
return None, None
class MouseJigglerGUI:
def __init__(self, root):
self.root = root
self.root.title("Navin's Mouse Jiggler")
self.root.geometry("500x500")
self.jiggler_process = None
self.is_running = False
# Main frame
main_frame = ttk.Frame(root, padding="20")
main_frame.pack(expand=True, fill='both')
# Auto-detect mouse device
device_path, device_name = find_mouse_device()
default_device = device_path if device_path else ""
# Device section
device_label_frame = ttk.LabelFrame(main_frame, text="Device Settings", padding="10")
device_label_frame.pack(fill='x', pady=(0, 15))
# Device path label and entry
ttk.Label(device_label_frame, text="Mouse Device Path:").pack(anchor='w')
self.device_var = tk.StringVar(value=default_device)
self.device_entry = ttk.Entry(device_label_frame, textvariable=self.device_var, width=40)
self.device_entry.pack(fill='x', pady=(5, 10))
# Detect button in its own frame
detect_frame = ttk.Frame(device_label_frame)
detect_frame.pack(fill='x')
self.detect_button = tk.Button(
detect_frame,
text="Detect Mouse Device",
command=self.detect_mouse,
width=20,
height=1,
bg='#ADD8E6', # Light blue
relief=tk.RAISED
)
self.detect_button.pack(pady=5)
# Movement settings section
settings_label_frame = ttk.LabelFrame(main_frame, text="Movement Settings", padding="10")
settings_label_frame.pack(fill='x', pady=(0, 15))
# Interval settings
ttk.Label(settings_label_frame, text="Interval (seconds):").pack(anchor='w')
self.interval_var = tk.StringVar(value="30")
self.interval_entry = ttk.Entry(settings_label_frame, textvariable=self.interval_var, width=10)
self.interval_entry.pack(pady=(5, 10))
# Distance settings
ttk.Label(settings_label_frame, text="Distance (pixels):").pack(anchor='w')
self.distance_var = tk.StringVar(value="10")
self.distance_entry = ttk.Entry(settings_label_frame, textvariable=self.distance_var, width=10)
self.distance_entry.pack(pady=(5, 10))
# Status section
status_frame = ttk.LabelFrame(main_frame, text="Status", padding="10")
status_frame.pack(fill='x', pady=(0, 15))
self.status_var = tk.StringVar(value="Status: Stopped")
self.status_label = ttk.Label(status_frame, textvariable=self.status_var)
self.status_label.pack(pady=5)
# Control buttons
button_frame = ttk.Frame(main_frame)
button_frame.pack(pady=10)
self.start_button = tk.Button(
button_frame,
text="Start Jiggler",
command=self.start_jiggler,
width=15,
height=2,
bg='#90EE90', # Light green
relief=tk.RAISED
)
self.start_button.pack(side=tk.LEFT, padx=10)
self.stop_button = tk.Button(
button_frame,
text="Stop Jiggler",
command=self.stop_jiggler,
width=15,
height=2,
bg='#FFB6C1', # Light red
relief=tk.RAISED,
state=tk.DISABLED
)
self.stop_button.pack(side=tk.LEFT, padx=10)
# Initial detection if no device found
if not default_device:
self.root.after(1000, self.detect_mouse) # Try to detect after GUI is shown
# Bind window close event
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
def detect_mouse(self):
device_path, device_name = find_mouse_device()
if device_path:
self.device_var.set(device_path)
messagebox.showinfo("Success", f"Mouse device found:\n{device_name}\nPath: {device_path}")
else:
messagebox.showerror("Error", "No mouse device found. Please ensure your mouse is connected and you have the necessary permissions.")
def validate_inputs(self):
try:
interval = float(self.interval_var.get())
distance = int(self.distance_var.get())
if interval <= 0 or distance <= 0:
raise ValueError
return True
except ValueError:
messagebox.showerror("Invalid Input", "Please enter valid numbers for interval and distance")
return False
def jiggler_loop(self):
while self.is_running:
try:
device = self.device_var.get()
interval = float(self.interval_var.get())
distance = self.distance_var.get()
# Move mouse right
subprocess.run([
"/usr/bin/evemu-event",
device,
"--type", "EV_REL",
"--code", "REL_X",
"--value", distance,
"--sync"
], check=True)
# Move mouse left
subprocess.run([
"/usr/bin/evemu-event",
device,
"--type", "EV_REL",
"--code", "REL_X",
"--value", f"-{distance}",
"--sync"
], check=True)
time.sleep(interval)
except subprocess.CalledProcessError:
self.is_running = False
self.root.after(0, self.handle_error)
break
def handle_error(self):
messagebox.showerror("Error", f"Failed to move mouse. Check if device path is correct and you have necessary permissions.")
self.stop_jiggler()
def start_jiggler(self):
if not self.validate_inputs():
return
if not os.path.exists(self.device_var.get()):
messagebox.showerror("Error", f"Device {self.device_var.get()} not found")
return
self.is_running = True
self.status_var.set("Status: Running")
self.start_button.configure(state=tk.DISABLED)
self.stop_button.configure(state=tk.NORMAL)
# Start the jiggler in a separate thread
self.jiggler_thread = threading.Thread(target=self.jiggler_loop, daemon=True)
self.jiggler_thread.start()
def stop_jiggler(self):
self.is_running = False
self.status_var.set("Status: Stopped")
self.start_button.configure(state=tk.NORMAL)
self.stop_button.configure(state=tk.DISABLED)
def on_closing(self):
if self.is_running:
self.stop_jiggler()
self.root.destroy()
sys.exit(0)
if __name__ == "__main__":
root = tk.Tk()
app = MouseJigglerGUI(root)
root.mainloop()