Created
September 24, 2025 03:43
-
-
Save jkassemi/300b4945aefe820a25dac0eb10558d1f to your computer and use it in GitHub Desktop.
Omarchy mouse acceleration configuration generation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # vibed by grok4 | |
| # macOS-like pointer acceleration configuration for Hyprland Omarchy, with device selection and config update between comments | |
| import subprocess | |
| import sys | |
| import os | |
| # Parameters | |
| sample_point_count = 100 # Smoothness (20 is balanced) | |
| low, mid, high = 0.9, 0.01, 0.002 # Gentler curve to reduce overshoot | |
| max_speed = 100 # Max input speed (units/ms) | |
| input_conf_path = os.path.expanduser("~/.config/hypr/input.conf") | |
| start_comment = "# BEGIN MOUSE ACCEL" | |
| end_comment = "# END MOUSE ACCEL" | |
| def macos_acceleration(speed_in): | |
| return low * speed_in + mid * speed_in**2 + high * speed_in**3 | |
| def generate_curve_points(count): | |
| step = max_speed / count | |
| speeds = [step * i for i in range(count)] | |
| factors = [macos_acceleration(s) for s in speeds] | |
| return [f"{f:.3f}" for f in factors], step | |
| def get_mouse_devices(): | |
| try: | |
| result = subprocess.run(["hyprctl", "devices"], capture_output=True, text=True, check=True) | |
| output = result.stdout | |
| devices = [] | |
| in_mice_section = False | |
| in_new_device_section = False | |
| for line in output.splitlines(): | |
| if line.strip() == "mice:": | |
| in_mice_section = True | |
| in_new_device_section = False | |
| continue | |
| elif in_mice_section and line.strip().startswith("Mouse at"): | |
| in_new_device_section = True | |
| continue | |
| elif in_new_device_section: | |
| in_new_device_section = False | |
| device_name = line.strip() | |
| devices.append(device_name) | |
| elif in_mice_section and not line.strip(): | |
| break | |
| return devices | |
| except subprocess.CalledProcessError as e: | |
| print(f"Error running hyprctl devices: {e}") | |
| sys.exit(1) | |
| def select_device(): | |
| devices = get_mouse_devices() | |
| if not devices: | |
| print("No mouse devices found. Check 'hyprctl devices'.") | |
| sys.exit(1) | |
| print("Available mouse devices:") | |
| for i, device in enumerate(devices, 1): | |
| print(f"{i}. {device}") | |
| while True: | |
| try: | |
| choice = int(input("Select a device (1-{}): ".format(len(devices)))) | |
| if 1 <= choice <= len(devices): | |
| return devices[choice - 1] | |
| print("Invalid choice. Try again.") | |
| except ValueError: | |
| print("Enter a number. Try again.") | |
| def hyprctl_set(device, option, arg): | |
| cmd = f"hyprctl keyword 'device[{device}]:{option}' '{arg}'" | |
| try: | |
| subprocess.run(cmd, shell=True, check=True) | |
| print(f"Applied {option} for {device}: {arg}") | |
| except subprocess.CalledProcessError as e: | |
| print(f"Error applying {option} for {device}: {e}") | |
| def update_input_conf(device, step, points): | |
| points_str = " ".join(points) | |
| device_block = f"""\ | |
| {start_comment} {device} | |
| device {{ | |
| name = {device} | |
| accel_profile = custom {step} {points_str} | |
| scroll_points = {step} {points_str} | |
| sensitivity = -0.1 | |
| }} | |
| {end_comment} {device} | |
| """ | |
| try: | |
| if not os.path.exists(input_conf_path): | |
| print(f"{input_conf_path} does not exist. Please add the following to it manually:") | |
| print(device_block) | |
| print(f"Ensure 'source = ~/.config/hypr/input.conf' is in ~/.config/hypr/hyprland.conf") | |
| return | |
| with open(input_conf_path, "r") as f: | |
| lines = f.readlines() | |
| # Find and replace between comments | |
| new_lines = [] | |
| i = 0 | |
| while i < len(lines): | |
| if lines[i].strip() == start_comment: | |
| # Skip existing block until end_comment | |
| while i < len(lines) and lines[i].strip() != end_comment: | |
| i += 1 | |
| if i < len(lines): | |
| i += 1 # Skip end_comment | |
| new_lines.append(device_block) | |
| else: | |
| new_lines.append(lines[i]) | |
| i += 1 | |
| # If comments not found, append block | |
| if start_comment not in ''.join(lines): | |
| print(f"{start_comment} not found in {input_conf_path}. Please add the following manually:") | |
| print(device_block) | |
| print(f"Ensure 'source = ~/.config/hypr/input.conf' is in ~/.config/hypr/hyprland.conf") | |
| new_lines.append("\n" + device_block) | |
| with open(input_conf_path, "w") as f: | |
| f.writelines(new_lines) | |
| print(f"Updated {input_conf_path} between {start_comment} and {end_comment}") | |
| except IOError as e: | |
| print(f"Error updating {input_conf_path}: {e}") | |
| def main(): | |
| points, step = generate_curve_points(sample_point_count) | |
| points_str = " ".join(points) | |
| print(f"Generated curve - Step: {step}, Points: {points_str}") | |
| device = None | |
| for arg in sys.argv[1:]: | |
| if arg.startswith("device="): | |
| device = arg[7:] | |
| break | |
| if not device: | |
| device = select_device() | |
| # Apply accel_profile and scroll_points | |
| hyprctl_set(device, "accel_profile", f"custom {step} {points_str}") | |
| hyprctl_set(device, "scroll_points", f"{step} {points_str}") | |
| # Update input.conf | |
| update_input_conf(device, step, points) | |
| print("Settings applied. Reload Hyprland with 'Super + Shift + R' or 'hyprctl reload' for full effect.") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment