Compare commits

...

15 Commits

Author SHA1 Message Date
97e6fdca0e disable some unused sway config parts, keep it more basic 2025-12-31 15:03:10 +02:00
af151a41af ensure firefox is default 2025-12-31 14:56:07 +02:00
9fcef4b4ed pavucontrol as floater 2025-12-31 14:56:00 +02:00
e405894cc2 improve structure; move stuff to relevant folders 2025-12-31 14:53:07 +02:00
e4296a3251 gamemode 1080p 330hz toggle 2025-12-30 22:48:38 +02:00
db810daf00 update more for 4k oled 2025-12-30 22:37:32 +02:00
bd0b49abce updates 2025-12-30 22:37:13 +02:00
0dabe39be9 updates 2025-12-09 21:23:12 +02:00
c70ad02033 test 2025-11-23 00:27:02 +02:00
5dab98c7b9 update 2025-10-29 12:36:16 +02:00
9c893c61b7 Merge branch 'master' of ssh://gitea.futur.ee:222/k4rli/sway-new-config 2025-09-24 19:35:35 +03:00
b8a6460dc0 swayimg as default image viewer 2025-08-16 03:16:22 +03:00
8394f6666c set thunar as file manager 2025-08-16 03:12:59 +03:00
723bf37d1f sway (+x11 for vibrance) config improvements 2025-08-16 03:09:09 +03:00
25d6e4f51b displays 2025-07-20 22:05:44 +03:00
43 changed files with 3889 additions and 139 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
cs2_benchmarks/
mobos/

6
docs/swap.md Normal file
View File

@@ -0,0 +1,6 @@
In case of zram swap:
Make changes persistent by adding to /etc/sysctl.conf or a new file in /etc/sysctl.d/:
vm.swappiness=100
vm.admin_reserve_kbytes=32768

View File

@@ -15,6 +15,7 @@ icons=0
max-icon-size=64
default-timeout=10000
ignore-timeout=1
on-notify=exec mpv /usr/share/sounds/freedesktop/stereo/message.oga
[urgency=normal]
border-color=#FFFFFF

View File

@@ -63,36 +63,59 @@ set $menu fuzzel
# File manager (Arch alternatives)
set $filemanager thunar
# Defaults (kept here so the config still loads even if the included file is missing)
set $dp1_mode 3840x2160@165Hz
set $dp1_bar_height 40
set $dp1_bar_font pango: FontAwesome, monospace 14
set $dp2_x 3840
set $dp2_bar_height 24
set $dp2_bar_font pango: FontAwesome, monospace 10
# Display/bar profile overrides for 330Hz gamemode
include /home/raga/.config/sway/scripts/display-profile/display-profile.conf
###############################################################################
# Output configuration
###############################################################################
# Default wallpaper (more resolutions are available in /usr/share/backgrounds/sway/)
# output * bg /usr/share/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
output * bg /home/raga/.config/sway/wallpaper.jpg fill
# output * bg /home/raga/.config/sway/wallpaper.jpg fill
# 144Hz monitor configuration
# Add custom 144Hz mode for your monitor
# Using the same timing as your xrandr command: 586.59 2560 2568 2600 2640 1440 1529 1537 1543 +hsync -vsync
output HDMI-A-1 {
modeline 586.59 2560 2568 2600 2640 1440 1529 1537 1543 +hsync -vsync
# Multi-monitor setup
# You can get your output names by running: swaymsg -t get_outputs
# DP-2 is the primary monitor on the left, and DP-1 is on the right.
output DP-1 {
adaptive_sync on
render_bit_depth 10
position 0 0
# ECO
# mode 2560x1440@60Hz
# FULL
mode $dp1_mode
}
# You can get the names of your outputs by running: swaymsg -t get_outputs
output DP-2 {
position $dp2_x 0
# ECO
# mode 2560x1440@60Hz
# FULL
# modeline 586.59 2560 2568 2600 2640 1440 1529 1537 1543 +hsync -vsync
}
###############################################################################
# Idle configuration
###############################################################################
# Lock screen after 900 seconds of inactivity, then turn off displays
#exec swayidle -w \
# timeout 900 'swaylock -f -c 000000' \
# timeout 1800 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
# before-sleep 'swaylock -f -c 000000'
# Lock screen after 900 seconds of inactivity
exec swayidle -w \
timeout 900 'swaylock -f -c 000000' \
# timeout 1800 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
before-sleep 'swaylock -f -c 000000'
###############################################################################
@@ -100,9 +123,13 @@ exec swayidle -w \
###############################################################################
# Estonian keyboard layout configuration
input type:keyboard {
xkb_layout "ee"
xkb_variant "nodeadkeys"
# input type:keyboard {
# xkb_layout "ee"
# xkb_variant "nodeadkeys"
# }
input * {
xkb_layout "ee,it"
# xkb_options "grp:alt_shift_toggle"
}
# Enable touchpad tap, natural scroll
@@ -125,6 +152,7 @@ input type:pointer {
# Floating window rules
floating_maximum_size -1 x -1
for_window [app_id="floating_window"] floating enable
for_window [app_id="pavucontrol"] floating enable
# Border configuration
default_floating_border pixel 1
@@ -153,6 +181,9 @@ floating_modifier $mod normal
# Launcher dialogs
###############################################################################
## Keyboard // Toggle Layout // <Alt><Shift> l ##
bindsym $alt+Shift+L input * xkb_switch_layout next
## Launch // Terminal // <> Enter ##
bindsym $mod+Return exec $term
@@ -188,26 +219,47 @@ bindsym $mod+Ctrl+r reload
bindsym $mod+Shift+e exec swaynag -t warning -m 'Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit'
## Session // Reboot // <><Shift> b ##
bindsym $mod+Shift+b exec systemctl reboot
#bindsym $mod+Shift+b exec systemctl reboot
## Session // Power Down // <><Shift> p ##
bindsym $mod+Shift+p exec systemctl poweroff
#bindsym $mod+Shift+p exec systemctl poweroff
## Session // Lock Screen // <> Escape ##
bindsym $mod+Escape exec swaylock -f -c 000000
## Session // Sleep // <><Shift> s ##
bindsym $mod+Shift+s exec systemctl suspend
#bindsym $mod+Shift+s exec systemctl suspend
###############################################################################
# Custom scripts (OSRS mode)
###############################################################################
set $osrs_scripts /home/raga/repos/linux/sway-new-config/scripts/osrs
set $osrs_mode OSRS
#bindsym f12 mode "$osrs_mode"
mode "$osrs_mode" {
bindsym q exec $osrs_scripts/osrs-drop.sh
bindsym w exec $osrs_scripts/osrs-click.sh
bindsym o exec $osrs_scripts/mouse-toggle.sh toggle
bindsym a exec kitty -e $osrs_scripts/autoclicker.sh
#bindsym Escape exec $osrs_scripts/autoclicker-stop.sh
## Resize // Exit OSRS Mode // Escape or Enter ##
bindsym Return mode "default"
bindsym Escape mode "default"
bindsym f12 mode "default"
}
###############################################################################
# System Management (Arch Linux alternatives)
###############################################################################
## Modify // Settings // <> c ##
bindsym $mod+c exec env XDG_CURRENT_DESKTOP=GNOME gnome-control-center
#bindsym $mod+c exec env XDG_CURRENT_DESKTOP=GNOME gnome-control-center
## Modify // Display Settings // <> d ##
bindsym $mod+d exec wdisplays
#bindsym $mod+d exec wdisplays
## Modify // Wifi Settings // <> w ##
bindsym $mod+w exec nm-connection-editor
@@ -288,7 +340,7 @@ bindsym $mod+Ctrl+Shift+Tab workspace prev_on_output
bindsym $mod+Ctrl+h workspace prev_on_output
## Navigate // Scratchpad // <><Ctrl> a ##
bindsym $mod+Ctrl+a scratchpad show
#bindsym $mod+Ctrl+a scratchpad show
###############################################################################
# Window and Workspace Configuration
@@ -319,10 +371,10 @@ bindsym $mod+Ctrl+Shift+k move workspace to output up
bindsym $mod+Ctrl+Shift+l move workspace to output right
## Modify // Vertical Window Orientation // <> v ##
bindsym $mod+v splitv
#bindsym $mod+v splitv
## Modify // Horizontal Window Orientation // <> g ##
bindsym $mod+g splith
#bindsym $mod+g splith
## Modify // Toggle Window Orientation // <> BackSpace ##
bindsym $mod+BackSpace split toggle
@@ -334,13 +386,13 @@ bindsym $mod+f fullscreen toggle
bindsym $mod+Shift+f floating toggle
## Modify // Move to Scratchpad // <><Ctrl> m ##
bindsym $mod+Ctrl+m move to scratchpad
#bindsym $mod+Ctrl+m move to scratchpad
## Modify // Tile/Float Focus Toggle // <><Shift> t ##
bindsym $mod+Shift+t focus mode_toggle
#bindsym $mod+Shift+t focus mode_toggle
## Modify // Window Layout Mode // <> t ##
bindsym $mod+t layout toggle tabbed splith splitv
#bindsym $mod+t layout toggle tabbed splith splitv
# Move focused container to workspace
## Modify // Move Window to Workspace 1 - 10 // <><Shift> 0..9 ##
@@ -391,14 +443,14 @@ bindsym $mod+$alt+$ws_high_key+$ws8_key move container to workspace number $ws18
bindsym $mod+$alt+$ws_high_key+$ws9_key move container to workspace number $ws19; workspace number $ws19
# Layout bindings (matching i3 defaults)
bindsym $mod+s layout stacking
bindsym $mod+e layout toggle split
#bindsym $mod+s layout stacking
#bindsym $mod+e layout toggle split
# Move the currently focused window to the scratchpad
bindsym $mod+Shift+minus move scratchpad
#bindsym $mod+Shift+minus move scratchpad
# Show the next scratchpad window or hide the focused scratchpad window.
bindsym $mod+minus scratchpad show
#bindsym $mod+minus scratchpad show
###############################################################################
# Resize Mode
@@ -414,10 +466,10 @@ mode "Resize Mode" {
bindsym Up resize shrink height 6 px or 6 ppt
bindsym Right resize grow width 6 px or 6 ppt
bindsym Shift+Left resize shrink width 24 px or 24 ppt
bindsym Shift+Down resize grow height 24 px or 24 ppt
bindsym Shift+Up resize shrink height 24 px or 24 ppt
bindsym Shift+Right resize grow width 24 px or 24 ppt
bindsym Shift+Left resize shrink width 48 px or 48 ppt
bindsym Shift+Down resize grow height 48 px or 48 ppt
bindsym Shift+Up resize shrink height 48 px or 48 ppt
bindsym Shift+Right resize grow width 48 px or 48 ppt
## Resize // Resize Window // k j h l ##
bindsym $left resize shrink width 6 px or 6 ppt
@@ -425,10 +477,10 @@ mode "Resize Mode" {
bindsym $up resize shrink height 6 px or 6 ppt
bindsym $right resize grow width 6 px or 6 ppt
bindsym Shift+$left resize shrink width 24 px or 24 ppt
bindsym Shift+$down resize grow height 24 px or 24 ppt
bindsym Shift+$up resize shrink height 24 px or 24 ppt
bindsym Shift+$right resize grow width 24 px or 24 ppt
bindsym Shift+$left resize shrink width 48 px or 48 ppt
bindsym Shift+$down resize grow height 48 px or 48 ppt
bindsym Shift+$up resize shrink height 48 px or 48 ppt
bindsym Shift+$right resize grow width 48 px or 48 ppt
## Resize // Exit Resize Mode // Escape or Enter ##
bindsym Return mode "default"
@@ -442,9 +494,9 @@ bindsym $mod+r mode "Resize Mode"
###############################################################################
# Volume control (works with both PulseAudio and PipeWire)
bindsym --locked XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ toggle && ~/.config/sway/volume-notify-sway.sh
bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5% && ~/.config/sway/volume-notify-sway.sh
bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5% && ~/.config/sway/volume-notify-sway.sh
bindsym --locked XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ toggle && ~/.config/sway/scripts/sway/volume-notify.sh
bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5% && ~/.config/sway/scripts/sway/volume-notify.sh
bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5% && ~/.config/sway/scripts/sway/volume-notify.sh
bindsym --locked XF86AudioMicMute exec pactl set-source-mute @DEFAULT_SOURCE@ toggle
# Brightness control
@@ -453,6 +505,7 @@ bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+
# Screenshots
bindsym Print exec grim - | swappy -f -
bindsym $mod+Print exec grim -g "$(slurp)" - | swappy -f -
## Modify // Toggle Bar // <> i ##
@@ -463,14 +516,15 @@ bindsym $mod+i bar mode toggle
###############################################################################
bar {
output DP-1
position bottom
mode dock
font pango:monospace 10
height $dp1_bar_height
font $dp1_bar_font
separator_symbol " "
strip_workspace_numbers yes
workspace_min_width 36
# Try to use i3status-rust for better functionality, fallback to basic status
status_command i3status-rs ~/.config/sway/i3status-rust.toml || while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done
colors {
@@ -480,9 +534,34 @@ bar {
# border backgr. text
focused_workspace #000000 #ffffff #000000
active_workspace #000000 #000000 #ffffff
active_workspace #ffffff #000000 #ffffff
inactive_workspace #000000 #000000 #ffffff
urgent_workspace #000000 #ff0000 #000000
urgent_workspace #000000 #ff0000 #ffffff
}
}
bar {
output DP-2
position bottom
mode dock
height $dp2_bar_height
font $dp2_bar_font
separator_symbol " "
strip_workspace_numbers yes
workspace_min_width 36
status_command i3status-rs ~/.config/sway/i3status-rust.toml || while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done
colors {
background #000000
statusline #ffffff
separator #666666
# border backgr. text
focused_workspace #000000 #ffffff #000000
active_workspace #ffffff #000000 #ffffff
inactive_workspace #000000 #000000 #ffffff
urgent_workspace #000000 #ff0000 #ffffff
}
}
@@ -501,6 +580,9 @@ exec lxqt-policykit-agent # Authentication agent (comment out if not needed)
exec --no-startup-id blueman-applet
exec --no-startup-id nm-applet
exec --no-startup-id home_dotfiles/.config/sway/scripts/i3status/set-random-bg.sh
# Include additional config files if they exist
include /etc/sway/config.d/*
#bindsym $mod+x exec "$(file="/tmp/click"; if test "0" = "$(cat "$file")"; then printf '1\n' > "$file"; else printf '0\n' > "$file"; fi)"

View File

@@ -1,9 +1,42 @@
# Advanced i3status-rust configuration
# Based on the i3xrocks blocks from your i3 setup
[[block]]
block = "custom"
command = "~/.config/sway/scripts/osrs/osrs-mode-status.sh"
format = " $text "
interval = 1
click = [
{button = "left", cmd = "~/.config/sway/scripts/osrs/osrs-mode-toggle.sh", update = true}
]
[[block]]
block = "custom"
command = "~/.config/sway/scripts/display-profile/display-profile-status.sh"
format = " $text "
interval = 1
click = [
{button = "left", cmd = "~/.config/sway/scripts/display-profile/display-profile-toggle.sh", update = true}
]
[[block]]
block = "custom"
command = "echo ''"
format = " $text "
interval = 1
click = [
{button = "left", cmd = "~/.config/sway/scripts/i3status/set-random-bg.sh", update = false},
]
#[[block]]
#block = "custom"
#command = "~/.config/sway/scripts/i3status/get-focused-window.sh"
#format = " $text "
#interval = 5
[[block]]
block = "net"
format = " $icon {$signal_strength $ssid $frequency|Wired connection} via $device "
format = " $icon {$signal_strength $ssid $frequency|Wired} @ $device "
interval = 1
[[block]]
@@ -16,6 +49,8 @@ format_alt = " $icon $frequency{ $boost} "
block = "memory"
format = " $icon $mem_used_percents.eng(w:2) "
interval = 10
warning_mem = 80.0
critical_mem = 90.0
[[block]]
block = "battery"
@@ -41,7 +76,7 @@ disconnected_format = ""
# Custom Bluetooth connected devices block
[[block]]
block = "custom"
command = "~/.config/sway/bluetooth-sway.sh"
command = "~/.config/sway/scripts/i3status/bluetooth.sh"
format = " $text "
interval = 10
click = [
@@ -52,7 +87,7 @@ click = [
# Custom weather block using your wttr.in script
# [[block]]
# block = "custom"
# command = "~/.config/sway/weather-sway.sh"
# command = "~/.config/sway/scripts/i3status/wttrin-weather.sh"
# format = " 🌤 $text "
# interval = 1800
# click = [
@@ -64,7 +99,7 @@ click = [
# Uncomment and modify this block if you want weather for a different city
[[block]]
block = "custom"
command = "~/.config/sway/weather-sway.sh"
command = "~/.config/sway/scripts/i3status/wttrin-weather.sh"
format = "$text"
interval = 1800
@@ -73,14 +108,14 @@ interval = 1800
# 1 = current conditions, 2 = current + today, 3 = current + today + tomorrow, 4 = current + 3 days
# [[block]]
# block = "custom"
# command = "weather_format='2' ~/.config/sway/weather-sway.sh"
# command = "weather_format='2' ~/.config/sway/scripts/i3status/wttrin-weather.sh"
# format = " 📅 $text "
# interval = 1800
[[block]]
block = "time"
format = " $icon $timestamp.datetime(f:'%Y-%m-%d %H:%M:%S') "
format = " $icon $timestamp.datetime(f:'%Y-%m-%d %H:%M:%S%z') "
interval = 1
[icons]
icons = "awesome4"
icons = "awesome6"

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
PROFILE_FILE="/home/raga/.config/sway/scripts/display-profile/display-profile.conf"
mode_str=""
if [[ -f "$PROFILE_FILE" ]]; then
mode_str="$(awk '$1=="set" && $2=="$dp1_mode" {print $3; found=1} END {if(!found) print ""}' "$PROFILE_FILE" 2>/dev/null)"
fi
# Display-related FontAwesome icon + a short label.
case "$mode_str" in
3840x2160@*|4096x2160@*) echo " 4K";;
1920x1080@*) echo " 1080p";;
"") echo "";;
*) echo "${mode_str}";;
esac

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
# Toggle DP-1 between 4K@165 and 1080p@330 and keep DP-2 positioned flush so
# you don't get a "dead gap" for the mouse.
PROFILE_FILE="/home/raga/.config/sway/scripts/display-profile/display-profile.conf"
MODE_4K="3840x2160@165Hz"
MODE_GAME="1920x1080@330Hz"
# Bar tuning per mode
BAR_HEIGHT_4K="40"
BAR_FONT_4K="pango: FontAwesome, monospace 14"
BAR_HEIGHT_GAME="16"
BAR_FONT_GAME="pango: FontAwesome, monospace 8"
# DP-2 bar settings (static)
DP2_BAR_HEIGHT="24"
DP2_BAR_FONT="pango: FontAwesome, monospace 10"
get_current_profile_mode() {
# Reads the last-written profile state instead of querying the monitor.
# This makes toggling deterministic even if the display reports odd modes.
if [[ ! -f "$PROFILE_FILE" ]]; then
echo "$MODE_4K"
return
fi
# Example line: set $dp1_mode 3840x2160@165Hz
awk '$1=="set" && $2=="$dp1_mode" {print $3; found=1} END {if(!found) exit 1}' "$PROFILE_FILE" 2>/dev/null \
|| echo "$MODE_4K"
}
write_profile() {
local dp1_mode="$1"
local dp1_bar_height="$2"
local dp1_bar_font="$3"
local dp2_x="$4"
local tmp
tmp="$(mktemp)"
cat >"$tmp" <<EOF
# Autogenerated by display-profile-toggle.sh. Manual edits will be overwritten.
set \$dp1_mode ${dp1_mode}
set \$dp1_bar_height ${dp1_bar_height}
set \$dp1_bar_font ${dp1_bar_font}
set \$dp2_x ${dp2_x}
set \$dp2_bar_height ${DP2_BAR_HEIGHT}
set \$dp2_bar_font ${DP2_BAR_FONT}
EOF
mkdir -p "$(dirname "$PROFILE_FILE")"
mv -f "$tmp" "$PROFILE_FILE"
}
main() {
local current_mode
current_mode="$(get_current_profile_mode)"
if [[ "$current_mode" == "$MODE_4K" ]]; then
# Switch to game mode (1080p @ high refresh)
write_profile "$MODE_GAME" "$BAR_HEIGHT_GAME" "$BAR_FONT_GAME" "1920"
else
# Switch back to 4K
write_profile "$MODE_4K" "$BAR_HEIGHT_4K" "$BAR_FONT_4K" "3840"
fi
swaymsg reload >/dev/null
}
main "$@"

View File

@@ -0,0 +1,12 @@
# Default display profile
# Rewritten and toggled in `display-profile-toggle.sh`
# DP-1 (4K / gaming output)
set $dp1_mode 3840x2160@165Hz
set $dp1_bar_height 40
set $dp1_bar_font pango: FontAwesome, monospace 14
# DP-2 (secondary output)
set $dp2_x 3840
set $dp2_bar_height 24
set $dp2_bar_font pango: FontAwesome, monospace 10

View File

@@ -59,4 +59,4 @@ elif [ "${BLOCK_BUTTON:-}" = "3" ]; then
else
notify-send "Bluetooth" "No Bluetooth manager found" -t 3000
fi
fi
fi

View File

@@ -0,0 +1,5 @@
#!/bin/bash
FOCUSED=$(swaymsg -t get_tree | jq '.. | select(.type?) | select(.focused==true) | .name')
echo "$FOCUSED"

View File

@@ -0,0 +1,32 @@
#!/bin/bash
# Find a random image file
#IMAGE=$(find /run/media/raga/970/qBittorrent/nsfw/xd -type f \( -name '*.png' -o -name '*.jpg' -o -name '*.jpeg' \) | shuf -n 1)
IMAGE=$(find /run/media/raga/970/images/cars/wallpaper -type f \( -name '*.png' -o -name '*.jpg' -o -name '*.jpeg' \) | shuf -n 1)
if [ -n "$IMAGE" ]; then
echo "Image: $IMAGE"
# Try to get image dimensions using identify (ImageMagick)
DIMENSIONS=$(identify -format "%wx%h" "$IMAGE" 2>/dev/null)
if [ -n "$DIMENSIONS" ]; then
echo "Dimensions: $DIMENSIONS"
WIDTH=$(echo "$DIMENSIONS" | cut -d'x' -f1)
HEIGHT=$(echo "$DIMENSIONS" | cut -d'x' -f2)
# Determine if portrait (height > width) or landscape
if [ "$HEIGHT" -gt "$WIDTH" ]; then
# Portrait image - use "fit" to maintain aspect ratio
echo "Portrait image - using fit"
swaymsg "output * bg \"$IMAGE\" fit"
else
# Landscape image - use "fill" to fill the screen
echo "Landscape image - using fill"
swaymsg "output * bg \"$IMAGE\" fill"
fi
else
# Fallback to fill if we can't determine dimensions
echo "Could not determine dimensions - using fill"
swaymsg "output * bg \"$IMAGE\" fill"
fi
fi

View File

@@ -0,0 +1,115 @@
#!/bin/bash
set -Eeu -o pipefail
# Improved weather script for i3status-rust using JSON API
# Based on the original weather script but optimized for Sway/i3status-rust
# Location (defaults to Pärnu as in your original script)
VALUE_WEATHER_LOCATION=${weather_location:-"Pärnu"}
VALUE_WEATHER_ERROR_MESSAGE=${error_message:-"⛔"}
# Determine units to use for temperature
# We don't supply a default here because wttr.in is "smart" enough to choose for us
WEATHER_UNIT=${weather_unit:-""}
if [ -n "${WEATHER_UNIT}" ]; then
WEATHER_UNIT="&${WEATHER_UNIT}"
fi
# Use JSON API to get all data in a single request
VALUE_FETCH_WEATHER_URL="https://wttr.in/${VALUE_WEATHER_LOCATION}?format=j1"
# Get weather data using JSON API
WEATHER_JSON=$(curl -sS "$VALUE_FETCH_WEATHER_URL" 2>/dev/null || echo "{}")
# Check for errors
if echo "$WEATHER_JSON" | grep -q "Unknown location"; then
echo "${VALUE_WEATHER_ERROR_MESSAGE}"
exit 0
fi
# Extract data using jq (should be available on most systems)
if command -v jq &>/dev/null; then
# Extract current weather condition and temperature
WEATHER_CONDITION=$(echo "$WEATHER_JSON" | jq -r '.current_condition[0].weatherDesc[0].value' 2>/dev/null || echo "")
TEMP_C=$(echo "$WEATHER_JSON" | jq -r '.current_condition[0].temp_C' 2>/dev/null || echo "")
# Extract sunrise and sunset times
SUNRISE=$(echo "$WEATHER_JSON" | jq -r '.weather[0].astronomy[0].sunrise' 2>/dev/null || echo "")
SUNSET=$(echo "$WEATHER_JSON" | jq -r '.weather[0].astronomy[0].sunset' 2>/dev/null || echo "")
# Convert to simple format if we have data
if [ -n "$WEATHER_CONDITION" ] && [ -n "$TEMP_C" ]; then
# Map weather conditions to icons (simplified)
case "$WEATHER_CONDITION" in
*"Sunny"*|*"Clear"*) ICON="☀️";;
*"Partly cloudy"*|*"Cloudy"*) ICON="⛅";;
*"Overcast"*) ICON="☁️";;
*"Rain"*|*"Drizzle"*) ICON="🌧️";;
*"Snow"*) ICON="❄️";;
*"Thunder"*) ICON="⛈️";;
*"Fog"*|*"Mist"*) ICON="🌫️";;
*) ICON="🌤️";;
esac
WEATHER="${ICON} ${TEMP_C}°C"
# Format sunrise/sunset times (convert from "09:10 AM" to "09:10")
if [ -n "$SUNRISE" ] && [ -n "$SUNSET" ]; then
# Remove AM/PM and convert to 24-hour format
SUNRISE_24=$(echo "$SUNRISE" | sed 's/ AM//;s/ PM//;s/^0//' | awk -F: '{if($1==12) print "00:"$2; else if($1<12) print $1":"$2; else print $1":"$2}')
SUNSET_24=$(echo "$SUNSET" | sed 's/ AM//;s/ PM//;s/^0//' | awk -F: '{if($1==12) print "00:"$2; else if($1<12) print $1":"$2; else print $1":"$2}')
# Remove leading zeros from hours for consistency
SUNRISE_24=$(echo "$SUNRISE_24" | sed 's/^0//')
SUNSET_24=$(echo "$SUNSET_24" | sed 's/^0//')
SUNRISE_SUNSET_TEXT=" (${SUNRISE_24}-${SUNSET_24})"
else
SUNRISE_SUNSET_TEXT=""
fi
# Output for i3status-rust (plain text)
echo "${WEATHER}${SUNRISE_SUNSET_TEXT}"
else
echo "${VALUE_WEATHER_ERROR_MESSAGE}"
fi
else
# Fallback to original method if jq is not available
echo "⚠️ jq not found, using fallback method"
# Get basic weather data
VALUE_WEATHER_FORMAT=${weather_format:-"%c%f"}
VALUE_WEATHER_FORMAT="?format=${VALUE_WEATHER_FORMAT}"
WEATHER=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_WEATHER_FORMAT}${WEATHER_UNIT}" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}")
# Get sunrise/sunset data
VALUE_SUNSET_SUNRISE_FORMAT="?format=%S-%s"
SUNRISE_SUNSET=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_SUNSET_SUNRISE_FORMAT}${WEATHER_UNIT}" 2>/dev/null || echo "")
if [ -n "$SUNRISE_SUNSET" ]; then
SUNRISE_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 1-5)
SUNSET_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 7-11)
SUNRISE_SUNSET_TEXT=" (${SUNRISE_VALUE}-${SUNSET_VALUE})"
else
SUNRISE_SUNSET_TEXT=""
fi
# Check for errors
if echo "${WEATHER}" | grep -q -P "Unknown\slocation"; then
WEATHER=${VALUE_WEATHER_ERROR_MESSAGE}
fi
# Output for i3status-rust (plain text)
echo "${WEATHER}${SUNRISE_SUNSET_TEXT}"
fi
# Handle click events (for i3status-rust custom block)
if [ "${BLOCK_BUTTON:-}" = "1" ]; then
# Left click - show detailed weather using JSON
DETAILED_WEATHER=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}?format=%l:+%c+%f+%h+%p+%P+%m+%w+%S-%s" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}")
notify-send "Weather Details" "$DETAILED_WEATHER" -t 10000
elif [ "${BLOCK_BUTTON:-}" = "3" ]; then
# Right click - open weather website
xdg-open "https://wttr.in/${VALUE_WEATHER_LOCATION}" >/dev/null 2>&1 &
fi

View File

@@ -0,0 +1,10 @@
#!/bin/bash
MODE=$(swaymsg -r -t get_binding_state 2>/dev/null | jq -r '.name // "default"')
ICON=""
if [ "$MODE" = "OSRS" ]; then
echo "$ICON ON"
else
echo "$ICON OFF"
fi

View File

@@ -0,0 +1,10 @@
#!/bin/bash
MODE_NAME="OSRS"
CURRENT=$(swaymsg -r -t get_binding_state 2>/dev/null | jq -r '.name // "default"')
if [ "$CURRENT" = "$MODE_NAME" ]; then
swaymsg mode default >/dev/null
else
swaymsg mode "$MODE_NAME" >/dev/null
fi

View File

@@ -11,4 +11,4 @@ if [ "$muted" = "yes" ]; then
notify-send -t 1000 "Volume" "Muted"
else
notify-send -t 1000 "Volume" "$volume"
fi
fi

View File

@@ -1,55 +0,0 @@
#!/bin/bash
set -Eeu -o pipefail
# Simplified weather script for i3status-rust
# Based on the original weather script but adapted for Sway/i3status-rust
# Information on the various formats: https://github.com/chubin/wttr.in
VALUE_WEATHER_FORMAT=${weather_format:-"%c%f"}
VALUE_WEATHER_FORMAT="?format=${VALUE_WEATHER_FORMAT}"
# Determine units to use for temperature
# We don't supply a default here because wttr.in is "smart" enough to choose for us
WEATHER_UNIT=${weather_unit:-""}
if [ -n "${WEATHER_UNIT}" ]; then
WEATHER_UNIT="&${WEATHER_UNIT}"
fi
# Location (defaults to Pärnu as in your original script)
VALUE_WEATHER_LOCATION=${weather_location:-"Pärnu"}
VALUE_WEATHER_ERROR_MESSAGE=${error_message:-"⛔"}
VALUE_FETCH_WEATHER_URL="https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_WEATHER_FORMAT}${WEATHER_UNIT}"
# Get weather data
WEATHER=$(curl -sS "$VALUE_FETCH_WEATHER_URL" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}")
# Get sunrise/sunset data
VALUE_SUNSET_SUNRISE_FORMAT="?format=%S-%s%20%m+"
SUNRISE_SUNSET=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}${VALUE_SUNSET_SUNRISE_FORMAT}${WEATHER_UNIT}" 2>/dev/null || echo "")
if [ -n "$SUNRISE_SUNSET" ]; then
SUNRISE_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 1-5)
SUNSET_VALUE=$(echo ${SUNRISE_SUNSET} | cut -c 10-14)
SUNRISE_SUNSET_TEXT=" (${SUNRISE_VALUE}-${SUNSET_VALUE})"
else
SUNRISE_SUNSET_TEXT=""
fi
# Check for errors
if echo "${WEATHER}" | grep -q -P "Unknown\slocation"; then
WEATHER=${VALUE_WEATHER_ERROR_MESSAGE}
fi
# Output for i3status-rust (plain text)
echo "${WEATHER}${SUNRISE_SUNSET_TEXT}"
# Handle click events (for i3status-rust custom block)
if [ "${BLOCK_BUTTON:-}" = "1" ]; then
# Left click - show detailed weather
FULL_WEATHER=$(curl -sS "https://wttr.in/${VALUE_WEATHER_LOCATION}?format=%l:+%c+%f+%h+%p+%P+%m+%w+%S+%s" 2>/dev/null || echo "${VALUE_WEATHER_ERROR_MESSAGE}")
notify-send "Weather Details" "$FULL_WEATHER" -t 10000
elif [ "${BLOCK_BUTTON:-}" = "3" ]; then
# Right click - open weather website
xdg-open "https://wttr.in/${VALUE_WEATHER_LOCATION}" >/dev/null 2>&1 &
fi

View File

@@ -0,0 +1,275 @@
# Swayimg configuration file.
# vim: filetype=dosini
# This file contains the default configuration.
# The viewer searches for the config file in the following locations:
# 1. $XDG_CONFIG_HOME/swayimg/config
# 2. $HOME/.config/swayimg/config
# 3. $XDG_CONFIG_DIRS/swayimg/config
# 4. /etc/xdg/swayimg/config
# Any of these options can be overridden using the --config argument
# on the command line, for instance:
# $ swayimg --config="general.mode=gallery"
################################################################################
# General configuration
################################################################################
[general]
# Mode at startup (viewer/slideshow/gallery)
mode = viewer
# Window size (fullscreen/image, or absolute size)
size = 1280,720
# Sway/Hyprland only: Window position (auto or absolute coordinates, e.g. 10,20)
position = auto
# Sway/Hyprland only: create floating window above the currently focused one
overlay = yes
# Use window decoration (yes/no)
decoration = no
# Action performed by SIGUSR1 signal (same format as for key bindings)
sigusr1 = reload
# Action performed by SIGUSR2 signal (same format as for key bindings)
sigusr2 = next_file
# Application ID and window class name
app_id = swayimg
################################################################################
# Viewer mode configuration
################################################################################
[viewer]
# Window background color (auto/extend/mirror/RGBA)
window = #00000000
# Background for transparent images (grid/RGBA)
transparency = grid
# Default image scale (optimal/width/height/fit/fill/real/keep)
scale = optimal
# Initial image position on the window (center/top/bottom/free/...)
position = center
# Anti-aliasing mode (none/box/bilinear/bicubic/mks13)
antialiasing = mks13
# Loop image list: jump to first image when end of list is reached (yes/no)
loop = yes
# Number of previously viewed images to store in cache
history = 1
# Number of preloaded images (read ahead)
preload = 1
################################################################################
# Slideshow mode configuration
################################################################################
[slideshow]
# Slideshow image display time (seconds)
time = 3
# Window background color (auto/extend/mirror/RGBA)
window = auto
# Background for transparent images (grid/RGBA)
transparency = #000000ff
# Default image scale (optimal/width/height/fit/fill/real)
scale = fit
# Initial image position on the window (center/top/bottom/free/...)
position = center
# Anti-aliasing mode (none/box/bilinear/bicubic/mks13)
antialiasing = mks13
################################################################################
# Gallery mode configuration
################################################################################
[gallery]
# Height and width of each thumbnail (pixels)
size = 200
# Max number of invisible thumbnails stored in memory cache
cache = 100
# Load not only visible but also adjacent thumbnails
preload = no
# Enable/disable storing thumbnails in persistent storage (yes/no)
pstore = no
# Fill the entire tile with thumbnail (yes/no)
fill = yes
# Anti-aliasing mode for thumbnails (none/box/bilinear/bicubic/mks13)
antialiasing = mks13
# Background color of the window (RGBA)
window = #00000000
# Background color of non-selected tiles (RGBA)
background = #202020ff
# Background color of the selected tile (RGBA)
select = #404040ff
# Border color of the selected tile (RGBA)
border = #000000ff
# Shadow color of the selected tile (RGBA)
shadow = #000000ff
################################################################################
# Image list configuration
################################################################################
[list]
# Default order (none/alpha/numeric/mtime/size/random)
order = alpha
# Reverse order (yes/no)
reverse = no
# Read directories recursively (yes/no)
recursive = no
# Add files from the same directory as the first file (yes/no)
all = no
# Enable file system monitoring for adding new images to the list (yes/no)
fsmon = yes
################################################################################
# Font configuration
################################################################################
[font]
# Font name
name = monospace
# Font size (pt)
size = 14
# Font color (RGBA)
color = #ccccccff
# Shadow color (RGBA)
shadow = #000000d0
# Background color (RGBA)
background = #00000000
################################################################################
# Image meta info scheme (format, size, EXIF, etc.)
################################################################################
[info]
# Show on startup (yes/no)
show = yes
# Timeout to hide info (seconds, 0 to always show)
info_timeout = 5
# Timeout to hide status message (seconds)
status_timeout = 3
# Display scheme for viewer mode (position = content)
[info.viewer]
top_left = +name,+format,+filesize,+imagesize,+exif
top_right = index
bottom_left = scale,frame
bottom_right = status
# Display scheme for slideshow mode (position = content)
[info.slideshow]
top_left = none
top_right = none
bottom_left = none
bottom_right = dir,status
# Display scheme for gallery mode (position = content)
[info.gallery]
top_left = none
top_right = index
bottom_left = none
bottom_right = name,status
################################################################################
# Viewer mode key binding configuration: key = action [parameters]
################################################################################
[keys.viewer]
F1 = help
Home = first_file
End = last_file
Prior = prev_file
Next = next_file
Space = next_file
Shift+r = rand_file
Shift+d = prev_dir
d = next_dir
Shift+o = prev_frame
o = next_frame
c = skip_file
s = mode slideshow
n = animation
f = fullscreen
Return = mode gallery
Left = step_left 10
Right = step_right 10
Up = step_up 10
Down = step_down 10
Equal = zoom +10
Plus = zoom +10
Minus = zoom -10
w = zoom width
Shift+w = zoom height
z = zoom fit
Shift+z = zoom fill
0 = zoom real
BackSpace = zoom optimal
k = zoom keep
Alt+s = zoom
bracketleft = rotate_left
bracketright = rotate_right
m = flip_vertical
Shift+m = flip_horizontal
a = antialiasing
r = reload
i = info
Shift+Delete = exec trash-put '%' && echo "File trashed: %"; skip_file
Escape = exit
q = exit
# Mouse related
ScrollLeft = step_right 5
ScrollRight = step_left 5
ScrollUp = step_up 5
ScrollDown = step_down 5
Ctrl+ScrollUp = zoom +10
Ctrl+ScrollDown = zoom -10
Shift+ScrollUp = prev_file
Shift+ScrollDown = next_file
Alt+ScrollUp = prev_frame
Alt+ScrollDown = next_frame
MouseLeft = drag
MouseSide = prev_file
MouseExtra = next_file
################################################################################
# Slideshow mode key binding configuration: key = action [parameters]
################################################################################
[keys.slideshow]
F1 = help
Home = first_file
End = last_file
Prior = prev_file
Next = next_file
Shift+r = rand_file
Shift+d = prev_dir
d = next_dir
Space = pause
i = info
f = fullscreen
Return = mode
Escape = exit
q = exit
################################################################################
# Gallery mode key binding configuration: key = action [parameters]
################################################################################
[keys.gallery]
F1 = help
Home = first_file
End = last_file
Left = step_left
Right = step_right
Up = step_up
Down = step_down
Prior = page_up
Next = page_down
c = skip_file
s = mode slideshow
f = fullscreen
Return = mode viewer
a = antialiasing
r = reload
i = info
Equal = thumb +20
Plus = thumb +20
Minus = thumb -20
Shift+Delete = exec trash-put '%' && echo "File trashed: %"; skip_file
Escape = exit
q = exit
# Mouse related
ScrollLeft = step_right
ScrollRight = step_left
ScrollUp = step_up
ScrollDown = step_down
Ctrl+ScrollUp = thumb +20
Ctrl+ScrollDown = thumb -20
MouseLeft = mode viewer

4
home_dotfiles/.profile Normal file
View File

@@ -0,0 +1,4 @@
if [ -d "$HOME/.local/bin" ]; then
PATH="$HOME/.local/bin:$PATH"
fi
export PATH

View File

@@ -147,3 +147,7 @@ alias info="pacman -Si "
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
source /usr/share/nvm/init-nvm.sh
source ~/.aliases
export JAVA_HOME=/usr/lib/jvm/java-21-temurin

View File

@@ -9,7 +9,7 @@ echo "Installing Oh My Zsh and related requirements..."
# Install base packages
echo "Installing base packages..."
sudo pacman -S \
sudo pacman -S --needed \
zsh \
git \
curl \

View File

@@ -52,6 +52,26 @@ else
exit 1
fi
# Copy .profile to home directory
echo ""
print_status "Installing .profile configuration..."
if cp "$SCRIPT_DIR/.profile" ~/.profile; then
print_success ".profile installed to ~/.profile"
else
print_error "Failed to copy .profile"
exit 1
fi
# Copy .aliases to home directory
echo ""
print_status "Installing .aliases configuration..."
if cp "$SCRIPT_DIR/.aliases" ~/.aliases; then
print_success ".aliases installed to ~/.aliases"
else
print_error "Failed to copy .aliases"
exit 1
fi
# Copy .p10k.zsh to home directory
echo ""
print_status "Installing Powerlevel10k configuration..."

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 MiB

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 MiB

2
install-arch-sway-requirements.sh Normal file → Executable file
View File

@@ -159,7 +159,7 @@ echo " - Default location: Pärnu (wttr.in, no API key needed)"
echo " - Shows weather + sunrise/sunset times"
echo " - Left click: detailed weather notification"
echo " - Right click: open wttr.in website"
echo " - Change location: edit ~/.config/sway/weather-sway.sh"
echo " - Change location: edit ~/.config/sway/scripts/i3status/wttrin-weather.sh"
echo ""
echo "3. Key applications configured:"
echo " - Terminal: foot"

View File

@@ -34,6 +34,12 @@ echo "Installing main Sway config..."
cp $SCRIPT_DIR/home_dotfiles/.config/sway/config ~/.config/sway/config
echo "✓ Sway config installed to ~/.config/sway/config"
# === SWAYIMG CONFIG ===
echo ""
echo "Installing swayimg config..."
cp $SCRIPT_DIR/home_dotfiles/.config/swayimg/swayimgrc ~/.config/swayimg/swayimgrc
echo "✓ Swayimg config installed to ~/.config/swayimg/swayimgrc"
# === I3STATUS-RUST CONFIG ===
# Comment out this section if you don't want i3status-rust
echo ""
@@ -41,34 +47,98 @@ echo "Installing i3status-rust config..."
cp $SCRIPT_DIR/home_dotfiles/.config/sway/i3status-rust.toml ~/.config/sway/i3status-rust.toml
echo "✓ i3status-rust config installed to ~/.config/sway/i3status-rust.toml"
# === WEATHER SCRIPT ===
# Comment out this section if you don't want the weather script
# === DISPLAY PROFILE TOGGLE (MODE / BAR HEIGHT / OUTPUT POSITION) ===
echo ""
echo "Installing weather script..."
cp $SCRIPT_DIR/home_dotfiles/.config/sway/weather-sway.sh ~/.config/sway/weather-sway.sh
chmod +x ~/.config/sway/weather-sway.sh
echo "✓ Weather script installed to ~/.config/sway/weather-sway.sh"
echo "Installing display profile toggle scripts/config..."
mkdir -p ~/.config/sway/scripts/display-profile
for file in display-profile.conf display-profile-toggle.sh display-profile-status.sh; do
if [ -f "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/display-profile/$file" ]; then
cp "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/display-profile/$file" ~/.config/sway/scripts/display-profile/$file
case "$file" in
*.sh)
chmod +x ~/.config/sway/scripts/display-profile/$file
;;
esac
echo "✓ Installed ~/.config/sway/scripts/display-profile/$file"
else
echo "$file not found, skipping..."
fi
done
# === BLUETOOTH SCRIPT ===
# === OSRS MODE SCRIPTS ===
echo ""
echo "Installing bluetooth script..."
cp $SCRIPT_DIR/home_dotfiles/.config/sway/bluetooth-sway.sh ~/.config/sway/bluetooth-sway.sh
chmod +x ~/.config/sway/bluetooth-sway.sh
echo "✓ Bluetooth script installed to ~/.config/sway/bluetooth-sway.sh"
echo "Installing OSRS mode helper scripts..."
mkdir -p ~/.config/sway/scripts/osrs
for script in osrs-mode-status.sh osrs-mode-toggle.sh; do
if [ -f "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/osrs/$script" ]; then
cp "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/osrs/$script" ~/.config/sway/scripts/osrs/$script
chmod +x ~/.config/sway/scripts/osrs/$script
echo "✓ Installed ~/.config/sway/scripts/osrs/$script"
else
echo "$script not found, skipping..."
fi
done
# === VOLUME NOTIFY SCRIPT ===
# Comment out this section if you don't want volume notifications
if [ -f volume-notify-sway.sh ]; then
echo ""
echo "Installing volume notify script..."
cp $SCRIPT_DIR/home_dotfiles/.config/sway/volume-notify-sway.sh ~/.config/sway/volume-notify-sway.sh
chmod +x ~/.config/sway/volume-notify-sway.sh
echo "✓ Volume notify script installed to ~/.config/sway/volume-notify-sway.sh"
# === i3status BAR SCRIPTS ===
echo ""
echo "Installing i3status bar scripts..."
mkdir -p ~/.config/sway/scripts/i3status
for script in bluetooth.sh wttrin-weather.sh set-random-bg.sh get-focused-window.sh; do
if [ -f "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/i3status/$script" ]; then
cp "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/i3status/$script" ~/.config/sway/scripts/i3status/$script
chmod +x ~/.config/sway/scripts/i3status/$script
echo "✓ Installed ~/.config/sway/scripts/i3status/$script"
else
echo "$script not found, skipping..."
fi
done
# === SWAY CONFIG SCRIPTS ===
echo ""
echo "Installing sway config scripts..."
mkdir -p ~/.config/sway/scripts/sway
for script in volume-notify.sh; do
if [ -f "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/sway/$script" ]; then
cp "$SCRIPT_DIR/home_dotfiles/.config/sway/scripts/sway/$script" ~/.config/sway/scripts/sway/$script
chmod +x ~/.config/sway/scripts/sway/$script
echo "✓ Installed ~/.config/sway/scripts/sway/$script"
else
echo "$script not found, skipping..."
fi
done
# === SET THUNAR AS DEFAULT FILE MANAGER ===
echo ""
echo "Setting thunar as default file manager..."
xdg-mime default thunar.desktop inode/directory application/x-gnome-saved-search
echo "✓ thunar set as default file manager"
# === SET SWAYIMG AS DEFAULT IMAGE VIEWER ===
echo ""
echo "Setting swayimg as default image viewer..."
xdg-mime default swayimg.desktop image/jpeg image/jpg image/png image/gif image/webp image/bmp image/tiff image/svg+xml
echo "✓ swayimg set as default image viewer"
# === SET FIREFOX AS DEFAULT BROWSER ===
echo ""
echo "Setting Firefox as default browser..."
if command -v firefox >/dev/null 2>&1; then
xdg-settings set default-web-browser firefox.desktop
echo "✓ Firefox set as default browser"
else
echo ""
echo "⚠ volume-notify-sway.sh not found, skipping..."
echo "⚠ Firefox not installed, skipping default browser setup"
fi
# === SET WALLPAPER ===
echo ""
echo "Setting wallpaper..."
WALLPAPER_NAME="chris-czermak-PamFFHL6fVY-unsplash.jpg"
#WALLPAPER_NAME="lucas-gallone-2dClJIAR404-unsplash.jpg"
#WALLPAPER_NAME="1742471076220983.jpg"
WALLPAPER_PATH="$SCRIPT_DIR/home_dotfiles/wallpapers/$WALLPAPER_NAME"
cp $WALLPAPER_PATH ~/.config/sway/wallpaper.jpg
echo "✓ Wallpaper installed to ~/.config/sway/wallpaper.jpg"
echo ""
echo "=========================================="
echo "✅ Sway configuration installation complete!"
@@ -82,12 +152,16 @@ echo ""
echo "Config files installed:"
echo "- ~/.config/mako/config (notification styling)"
echo "- ~/.config/sway/config (main Sway config)"
echo "- ~/.config/swayimg/swayimgrc (image viewer config)"
echo "- ~/.config/sway/i3status-rust.toml (status bar config)"
echo "- ~/.config/sway/weather-sway.sh (weather script)"
if [ -f ~/.config/sway/volume-notify-sway.sh ]; then
echo "- ~/.config/sway/volume-notify-sway.sh (volume notifications)"
echo "- ~/.config/sway/scripts/display-profile/display-profile.conf (display profile state/overrides)"
echo "- ~/.config/sway/scripts/display-profile/display-profile-toggle.sh (click-to-toggle display profile)"
echo "- ~/.config/sway/scripts/display-profile/display-profile-status.sh (status bar label for display profile)"
echo "- ~/.config/sway/scripts/i3status/wttrin-weather.sh (weather script)"
echo "- thunar set as default file manager"
echo "- swayimg set as default image viewer"
if [ -f ~/.config/sway/scripts/sway/volume-notify.sh ]; then
echo "- ~/.config/sway/scripts/sway/volume-notify.sh (volume notifications)"
fi
cp chris-czermak-PamFFHL6fVY-unsplash.jpg ~/.config/sway/wallpaper.jpg
makoctl reload

View File

@@ -0,0 +1,10 @@
#!/bin/bash
#gpg --full-generate-key
#gpg --list-secret-keys --keyid-format=long
#gpg --armor --export ..
#git config --global commit.gpgsign true

View File

@@ -0,0 +1,13 @@
[Unit]
Description=AMD GPU High Performance Mode
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'echo high > /sys/class/drm/card0/device/power_dpm_force_performance_level'
ExecStart=/bin/bash -c 'echo 2 > /sys/class/drm/card0/device/pp_dpm_sclk'
ExecStart=/bin/bash -c 'echo 3 > /sys/class/drm/card0/device/pp_dpm_mclk'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,85 @@
#!/bin/bash
# Comprehensive AMD GPU Performance Fix for Benchmarking
# Colors for output
RED='\033[0:31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}=== AMD RX 6900 XT Benchmark Performance Fix ===${NC}"
echo ""
# Find the AMD GPU
GPU_PATH=""
for card in /sys/class/drm/card[0-9]; do
if [ -f "$card/device/power_dpm_force_performance_level" ]; then
GPU_PATH="$card/device"
GPU_CARD=$(basename $(dirname $GPU_PATH))
break
fi
done
if [ -z "$GPU_PATH" ]; then
echo -e "${RED}ERROR: Could not find AMD GPU!${NC}"
exit 1
fi
echo -e "${GREEN}Found GPU at: $GPU_PATH${NC}"
echo ""
# Show current state
echo "=== BEFORE ==="
echo "Performance Level: $(cat $GPU_PATH/power_dpm_force_performance_level)"
echo "Core Clock:"
cat $GPU_PATH/pp_dpm_sclk | grep "\*"
echo "Memory Clock:"
cat $GPU_PATH/pp_dpm_mclk | grep "\*"
echo ""
# Apply fixes
echo -e "${YELLOW}Applying performance fixes...${NC}"
# 1. Set performance level to high
echo "high" | sudo tee $GPU_PATH/power_dpm_force_performance_level > /dev/null
echo "✓ Set performance level to HIGH"
# 2. Force highest core clock
echo "2" | sudo tee $GPU_PATH/pp_dpm_sclk > /dev/null
echo "✓ Forced highest core clock (2600MHz)"
# 3. Force highest memory clock
echo "3" | sudo tee $GPU_PATH/pp_dpm_mclk > /dev/null
echo "✓ Forced highest memory clock (2000MHz effective)"
# 4. Set power limit to maximum if available
if [ -f "$GPU_PATH/hwmon/hwmon*/power1_cap_max" ]; then
MAX_POWER=$(cat $GPU_PATH/hwmon/hwmon*/power1_cap_max)
echo "$MAX_POWER" | sudo tee $GPU_PATH/hwmon/hwmon*/power1_cap > /dev/null 2>&1
echo "✓ Set power limit to maximum"
fi
echo ""
echo "=== AFTER ==="
echo "Performance Level: $(cat $GPU_PATH/power_dpm_force_performance_level)"
echo "Core Clock:"
cat $GPU_PATH/pp_dpm_sclk | grep "\*"
echo "Memory Clock:"
cat $GPU_PATH/pp_dpm_mclk | grep "\*"
echo ""
echo -e "${GREEN}GPU is now at maximum performance!${NC}"
echo ""
echo -e "${YELLOW}Additional tips:${NC}"
echo "1. Close all background applications"
echo "2. Make sure Sway compositor isn't limiting FPS"
echo "3. Run benchmark in fullscreen mode"
echo "4. Check CPU governor is set to 'performance'"
echo ""
echo "To check CPU governor:"
echo " cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor | head -1"
echo ""
echo "To set CPU to performance mode:"
echo " echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor"
echo ""
echo -e "${GREEN}Now run your benchmark!${NC}"

View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Force AMD GPU to high performance mode for gaming/benchmarking
# Find the AMD GPU card (not integrated graphics)
GPU_PATH=""
for card in /sys/class/drm/card[0-9]; do
if [ -f "$card/device/power_dpm_force_performance_level" ]; then
GPU_PATH="$card/device"
break
fi
done
if [ -z "$GPU_PATH" ]; then
echo "ERROR: Could not find AMD GPU!"
exit 1
fi
echo "Found GPU at: $GPU_PATH"
echo "=== AMD GPU Performance Mode Script ==="
echo ""
# Check current state
echo "Current settings:"
echo "Performance Level: $(cat $GPU_PATH/power_dpm_force_performance_level)"
echo ""
echo "Core Clock States:"
cat $GPU_PATH/pp_dpm_sclk
echo ""
echo "Memory Clock States:"
cat $GPU_PATH/pp_dpm_mclk
echo ""
# Force high performance
echo "Forcing high performance mode..."
echo "high" | sudo tee $GPU_PATH/power_dpm_force_performance_level
# Force highest clock states
echo "Forcing maximum clock states..."
echo "2" | sudo tee $GPU_PATH/pp_dpm_sclk
echo "3" | sudo tee $GPU_PATH/pp_dpm_mclk
echo ""
echo "=== New settings ==="
echo "Performance Level: $(cat $GPU_PATH/power_dpm_force_performance_level)"
echo ""
echo "Core Clock States:"
cat $GPU_PATH/pp_dpm_sclk
echo ""
echo "Memory Clock States:"
cat $GPU_PATH/pp_dpm_mclk
echo ""
echo "GPU should now run at maximum performance!"
echo "Run your benchmark now."
echo ""
echo "To revert to auto mode, run:"
echo "echo 'auto' | sudo tee $GPU_PATH/power_dpm_force_performance_level"

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Script to add 144Hz mode to i3
echo "Adding 144Hz mode to i3..."
DISPLAY="DisplayPort-0"
MODE="2560x1440_144.00_rb2"
xrandr --newmode $MODE 586.59 2560 2568 2600 2640 1440 1529 1537 1543 +hsync -vsync
xrandr --addmode $DISPLAY $MODE
xrandr --output $DISPLAY --mode $MODE
echo "144Hz mode should now be active. Check with: xrandr"

View File

@@ -0,0 +1,5 @@
#!/bin/bash
xrandr \
--output HDMI-A-0 --primary --pos 0x0 \
--output DisplayPort-0 --pos 2560x0

View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Script to stop the autoclicker
STATE_FILE="/tmp/autoclicker_running"
if [ -f "$STATE_FILE" ]; then
rm -f "$STATE_FILE"
echo "Autoclicker stop signal sent!"
else
echo "Autoclicker is not running."
fi

135
scripts/osrs/autoclicker.sh Executable file
View File

@@ -0,0 +1,135 @@
#!/bin/bash
# Autoclicker script for Sway
# Clicks continuously with random delays and periodic breaks
# Press ESC to stop
# Requires ydotool and ydotoold daemon running
# Configuration
MIN_DELAY=51 # Minimum delay in milliseconds
MAX_DELAY=689 # Maximum delay in milliseconds
MIN_CLICKS_BEFORE_BREAK=50 # Minimum clicks before break
MAX_CLICKS_BEFORE_BREAK=300 # Maximum clicks before break
MIN_BREAK=5 # Minimum break duration in seconds
MAX_BREAK=10 # Maximum break duration in seconds
STATE_FILE="/tmp/autoclicker_running"
LOCK_FILE="/tmp/autoclicker_lock"
# Check if ydotool is available
if ! command -v ydotool &> /dev/null; then
echo "Error: ydotool is not installed. Install it with: sudo pacman -S ydotool"
exit 1
fi
# Check if bc is available (needed for random delay calculation)
if ! command -v bc &> /dev/null; then
echo "Error: bc is not installed. Install it with: sudo pacman -S bc"
exit 1
fi
# Check if script is already running (prevent multiple instances)
if [ -f "$LOCK_FILE" ]; then
echo "Autoclicker is already running!"
echo "Press ESC to stop it."
exit 0
fi
# Create lock file
echo $$ > "$LOCK_FILE"
# Clean up lock file on exit
cleanup() {
rm -f "$LOCK_FILE"
rm -f "$STATE_FILE"
echo ""
echo "Autoclicker stopped."
}
trap cleanup EXIT INT TERM
# Check if ydotoold daemon is running
if ! pgrep -x "ydotoold" > /dev/null; then
echo "Starting ydotoold daemon..."
sudo ydotoold &
sleep 1
fi
# Create state file to indicate running
echo "running" > "$STATE_FILE"
# Function to generate random delay in seconds
get_random_delay() {
local min=$1
local max=$2
local range=$((max - min))
local random_ms=$((min + RANDOM % (range + 1)))
echo "scale=3; $random_ms / 1000" | bc
}
# Function to generate random number of clicks before break
get_random_clicks() {
local min=$1
local max=$2
local range=$((max - min))
echo $((min + RANDOM % (range + 1)))
}
# Function to check if ESC key was pressed (by checking if state file was removed)
check_exit() {
if [ ! -f "$STATE_FILE" ]; then
echo "Exit signal received!"
exit 0
fi
}
echo "=========================================="
echo "Autoclicker started!"
echo "=========================================="
echo "Configuration:"
echo " - Click delay: ${MIN_DELAY}-${MAX_DELAY}ms"
echo " - Break every: ${MIN_CLICKS_BEFORE_BREAK}-${MAX_CLICKS_BEFORE_BREAK} clicks (random)"
echo " - Break duration: ${MIN_BREAK}-${MAX_BREAK}s"
echo ""
echo "Press ESC or run: rm $STATE_FILE"
echo "=========================================="
echo ""
click_count=0
total_clicks=0
clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK)
echo "Next break will be after $clicks_before_break clicks"
echo ""
while true; do
check_exit
# Generate random delay before this click
delay=$(get_random_delay $MIN_DELAY $MAX_DELAY)
# Perform click (suppress ydotool output)
ydotool click 0xC0 2>/dev/null
click_count=$((click_count + 1))
total_clicks=$((total_clicks + 1))
# Convert delay to milliseconds for display
delay_ms=$(echo "$delay * 1000" | bc | cut -d'.' -f1)
# Show each click with its delay
echo "Click #$total_clicks (delay: ${delay_ms}ms, next break in $((clicks_before_break - click_count)) clicks)"
# Check if it's time for a break
if [ $click_count -ge $clicks_before_break ]; then
break_duration=$(get_random_delay $((MIN_BREAK * 1000)) $((MAX_BREAK * 1000)))
echo ""
echo "[Break] Completed $clicks_before_break clicks (total: $total_clicks). Taking a break for ${break_duration}s..."
sleep "$break_duration"
click_count=0
# Generate new random click count for next cycle
clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK)
echo "[Resumed] Next break will be after $clicks_before_break clicks"
echo ""
else
# Wait the random delay before next click
sleep "$delay"
fi
done

View File

@@ -0,0 +1,235 @@
#!/bin/bash
# Enhanced Autoclicker script for Sway with improved antiban features
# Uses ydotool for Wayland/Sway environments
# Features human-like timing patterns and CSV logging for visualization
# Press Ctrl+C to stop (standard shell interrupt)
# Requires ydotool and ydotoold daemon running
# Configuration
MIN_DELAY=50 # Minimum delay in milliseconds
MAX_DELAY=700 # Maximum delay in milliseconds
MIN_CLICKS_BEFORE_BREAK=30 # Minimum clicks before break
MAX_CLICKS_BEFORE_BREAK=200 # Maximum clicks before break
MIN_BREAK=3 # Minimum break duration in seconds
MAX_BREAK=15 # Maximum break duration in seconds
# Human-like timing parameters
FASTIGUE_FACTOR=0.95 # Gradually slow down over time (0.95 = 5% slower)
REACTION_VARIATION=0.3 # 30% variation in reaction times
PATTERN_BREAK_CHANCE=0.05 # 5% chance to break pattern
# File paths
STATE_FILE="/tmp/autoclicker_running"
LOCK_FILE="/tmp/autoclicker_lock"
LOG_DIR="/tmp/autoclicker_logs"
LOG_FILE="$LOG_DIR/click_timings_$(date +%Y%m%d_%H%M%S).csv"
# Create log directory if it doesn't exist
mkdir -p "$LOG_DIR"
# Initialize CSV log file with header
echo "click_number,delay_ms,timestamp" > "$LOG_FILE"
# Check if ydotool is available
if ! command -v ydotool &> /dev/null; then
echo "Error: ydotool is not installed. Install it with: sudo pacman -S ydotool"
exit 1
fi
# Check if bc is available (needed for calculations)
if ! command -v bc &> /dev/null; then
echo "Error: bc is not installed. Install it with: sudo pacman -S bc"
exit 1
fi
# Check if script is already running (prevent multiple instances)
if [ -f "$LOCK_FILE" ]; then
echo "Autoclicker is already running!"
echo "Press Ctrl+C in the terminal where it's running to stop it."
# Clean up the incomplete log file that was just created
rm -f "$LOG_FILE"
exit 0
fi
# Create lock file
echo $$ > "$LOCK_FILE"
# Global exit flag
EXIT_REQUESTED=0
# Clean up on exit
cleanup() {
EXIT_REQUESTED=1
rm -f "$LOCK_FILE"
rm -f "$STATE_FILE"
echo ""
echo "Autoclicker stopped."
exit 0
}
# Set up traps for cleanup
trap cleanup INT TERM
# Check if ydotoold daemon is running
if ! pgrep -x "ydotoold" > /dev/null; then
echo "Starting ydotoold daemon..."
sudo ydotoold &
sleep 1
fi
# Create state file to indicate running
echo "running" > "$STATE_FILE"
# Function to log click timing to CSV
log_click_timing() {
local click_number=$1
local delay_ms=$2
local timestamp=$(date +%s%3N) # Milliseconds since epoch
echo "$click_number,$delay_ms,$timestamp" >> "$LOG_FILE"
}
# Function to generate random delay with human-like distribution
get_human_delay() {
local min=$1
local max=$2
local base_delay=$((min + RANDOM % (max - min + 1)))
# Apply reaction time variation (30% up or down)
local variation=$(echo "scale=3; $base_delay * $REACTION_VARIATION * (2 * $RANDOM / 32767 - 1)" | bc)
local varied_delay=$(echo "$base_delay + $variation" | bc)
# Ensure we stay within reasonable bounds
if (( $(echo "$varied_delay < $min" | bc -l) )); then
varied_delay=$min
elif (( $(echo "$varied_delay > $max" | bc -l) )); then
varied_delay=$max
fi
# Convert to seconds for sleep function
echo "scale=3; $varied_delay / 1000" | bc
}
# Function to generate random number of clicks before break
get_random_clicks() {
local min=$1
local max=$2
echo $((min + RANDOM % (max - min + 1)))
}
# Function to sleep with interrupt checking
interruptible_sleep() {
local duration=$1
local start_time=$(date +%s.%N)
local end_time=$(echo "$start_time + $duration" | bc)
while true; do
if [ $EXIT_REQUESTED -eq 1 ]; then
return 1 # Exit requested
fi
local current_time=$(date +%s.%N)
if (( $(echo "$current_time >= $end_time" | bc -l) )); then
return 0 # Sleep completed
fi
sleep 0.1
done
}
# Function to apply fatigue effect (gradually slow down)
apply_fatigue() {
local current_delay=$1
local click_count=$2
local fatigue_effect=$(echo "scale=3; $current_delay * (1 + $FASTIGUE_FACTOR * $click_count / 1000)" | bc)
echo "$fatigue_effect"
}
# Function to randomly break pattern (5% chance)
should_break_pattern() {
local threshold=$(echo "$PATTERN_BREAK_CHANCE * 32767" | bc | cut -d'.' -f1)
[ $RANDOM -lt $threshold ]
}
echo "=========================================="
echo "Enhanced Autoclicker started!"
echo "=========================================="
echo "Configuration:"
echo " - Click delay: ${MIN_DELAY}-${MAX_DELAY}ms (human-like variation)"
echo " - Break every: ${MIN_CLICKS_BEFORE_BREAK}-${MAX_CLICKS_BEFORE_BREAK} clicks (random)"
echo " - Break duration: ${MIN_BREAK}-${MAX_BREAK}s"
echo " - Fatigue factor: ${FASTIGUE_FACTOR} (gradual slowdown)"
echo " - Pattern break chance: ${PATTERN_BREAK_CHANCE} (5%)"
echo " - Logging to: $LOG_FILE"
echo ""
echo "Press Ctrl+C to stop"
echo "=========================================="
echo ""
click_count=0
total_clicks=0
clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK)
echo "Next break will be after $clicks_before_break clicks"
echo ""
while true; do
if [ $EXIT_REQUESTED -eq 1 ]; then
echo "Exit signal received!"
break
fi
# Generate base random delay
base_delay=$(get_human_delay $MIN_DELAY $MAX_DELAY)
# Apply fatigue effect (gradually slow down)
final_delay=$(apply_fatigue "$base_delay" "$click_count")
# Randomly break pattern (5% chance)
if should_break_pattern; then
# Add a longer, unpredictable delay
pattern_break_delay=$(echo "scale=3; ($MAX_DELAY * 2 + $RANDOM % 1000) / 1000" | bc)
echo "[Pattern Break] Adding unpredictable delay of $(echo "$pattern_break_delay * 1000" | bc | cut -d'.' -f1)ms"
if ! interruptible_sleep "$pattern_break_delay"; then
break
fi
fi
# Perform click (suppress ydotool output)
ydotool click 0xC0 2>/dev/null
click_count=$((click_count + 1))
total_clicks=$((total_clicks + 1))
# Convert delay to milliseconds for display
delay_ms=$(echo "$final_delay * 1000" | bc | cut -d'.' -f1)
# Log click timing to CSV
log_click_timing "$total_clicks" "$delay_ms"
# Show each click with its delay
echo "Click #$total_clicks (delay: ${delay_ms}ms, next break in $((clicks_before_break - click_count)) clicks)"
# Check if it's time for a break
if [ $click_count -ge $clicks_before_break ]; then
break_duration=$(get_human_delay $((MIN_BREAK * 1000)) $((MAX_BREAK * 1000)))
echo ""
echo "[Break] Completed $clicks_before_break clicks (total: $total_clicks). Taking a break for ${break_duration}s..."
if ! interruptible_sleep "$break_duration"; then
break
fi
click_count=0
# Generate new random click count for next cycle
clicks_before_break=$(get_random_clicks $MIN_CLICKS_BEFORE_BREAK $MAX_CLICKS_BEFORE_BREAK)
echo "[Resumed] Next break will be after $clicks_before_break clicks"
echo ""
else
# Wait the calculated delay before next click
if ! interruptible_sleep "$final_delay"; then
break
fi
fi
done
echo "Click timings saved to: $LOG_FILE"

View File

@@ -0,0 +1,619 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Autoclicker Timing Visualizer</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
}
.controls {
display: flex;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.control-group {
flex: 1;
min-width: 250px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #2c3e50;
}
input, select, button {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
width: 100%;
box-sizing: border-box;
}
button {
background-color: #3498db;
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s;
font-weight: 600;
}
button:hover {
background-color: #2980b9;
}
button:disabled {
background-color: #95a5a6;
cursor: not-allowed;
}
.stats {
display: flex;
gap: 20px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.stat-card {
flex: 1;
min-width: 200px;
background-color: #ecf0f1;
padding: 15px;
border-radius: 6px;
text-align: center;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #2c3e50;
margin: 5px 0;
}
.stat-label {
font-size: 12px;
color: #7f8c8d;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.chart-container {
background-color: white;
border-radius: 6px;
padding: 15px;
margin-bottom: 20px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
position: relative;
height: 400px;
}
.chart-title {
font-weight: 600;
margin-bottom: 10px;
color: #2c3e50;
}
.chart-wrapper {
position: relative;
height: 300px;
width: 100%;
}
.file-list {
margin-top: 20px;
}
.file-item {
padding: 10px;
border-bottom: 1px solid #eee;
cursor: pointer;
transition: background-color 0.2s;
}
.file-item:hover {
background-color: #f8f9fa;
}
.file-item.active {
background-color: #e3f2fd;
font-weight: 600;
}
.file-name {
color: #3498db;
margin-bottom: 3px;
}
.file-info {
font-size: 12px;
color: #7f8c8d;
}
.loading {
text-align: center;
padding: 20px;
color: #7f8c8d;
font-style: italic;
}
.error {
color: #e74c3c;
padding: 10px;
background-color: #fadbd8;
border-radius: 4px;
margin: 10px 0;
}
.server-info {
background-color: #e8f5e9;
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
border-left: 4px solid #4caf50;
}
.api-status {
background-color: #fff3cd;
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
display: none;
}
.api-status.success {
background-color: #d4edda;
color: #155724;
display: block;
}
.api-status.error {
background-color: #f8d7da;
color: #721c24;
display: block;
}
@media (max-width: 768px) {
.controls, .stats {
flex-direction: column;
}
}
</style>
</head>
<body>
<div class="container">
<h1>🖱️ Autoclicker Timing Visualizer</h1>
<div class="server-info">
<strong>📊 Real-time Data Server</strong>
<p>This visualizer connects to a local Python server that serves real log files from <code>/tmp/autoclicker_logs/</code></p>
<p>Make sure to run: <code>python3 click_server.py</code> in a separate terminal</p>
</div>
<div id="apiStatus" class="api-status">
Testing API connection...
</div>
<div class="controls">
<div class="control-group">
<label for="logFile">Select Log File:</label>
<select id="logFile">
<option value="">-- Loading log files... --</option>
</select>
</div>
<div class="control-group">
<label for="chartType">Chart Type:</label>
<select id="chartType">
<option value="scatter">Scatter Plot</option>
<option value="line">Line Chart</option>
<option value="histogram">Histogram</option>
</select>
</div>
<div class="control-group">
<label>&nbsp;</label>
<button id="loadBtn" disabled>Load Data</button>
</div>
</div>
<div class="stats" id="statsContainer" style="display: none;">
<div class="stat-card">
<div class="stat-label">Total Clicks</div>
<div class="stat-value" id="totalClicks">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Average Delay</div>
<div class="stat-value" id="avgDelay">0 ms</div>
</div>
<div class="stat-card">
<div class="stat-label">Min Delay</div>
<div class="stat-value" id="minDelay">0 ms</div>
</div>
<div class="stat-card">
<div class="stat-label">Max Delay</div>
<div class="stat-value" id="maxDelay">0 ms</div>
</div>
<div class="stat-card">
<div class="stat-label">Std Deviation</div>
<div class="stat-value" id="stdDev">0 ms</div>
</div>
</div>
<div class="chart-container">
<div class="chart-title">Click Timing Distribution</div>
<div class="chart-wrapper">
<canvas id="timingChart"></canvas>
</div>
</div>
<div class="chart-container">
<div class="chart-title">Delay Over Time</div>
<div class="chart-wrapper">
<canvas id="timeSeriesChart"></canvas>
</div>
</div>
<div class="file-list">
<h3>Available Log Files</h3>
<div id="fileList">
<div class="loading">Loading log files from server...</div>
</div>
</div>
</div>
<!-- Load Chart.js with date adapter -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0"></script>
<script>
// Global variables
let clickData = [];
let timingChart, timeSeriesChart;
let currentFile = '';
// API base URL
const API_BASE = '/api';
// DOM elements
const logFileSelect = document.getElementById('logFile');
const chartTypeSelect = document.getElementById('chartType');
const loadBtn = document.getElementById('loadBtn');
const fileList = document.getElementById('fileList');
const statsContainer = document.getElementById('statsContainer');
const apiStatus = document.getElementById('apiStatus');
// Test API connection
async function testAPIConnection() {
try {
const response = await fetch(`${API_BASE}/logs`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
if (response.ok) {
apiStatus.textContent = "✅ API connected successfully!";
apiStatus.className = "api-status success";
return true;
} else {
throw new Error(`HTTP ${response.status}`);
}
} catch (error) {
apiStatus.textContent = `❌ API connection failed: ${error.message}. Is the Python server running?`;
apiStatus.className = "api-status error";
console.error('API connection error:', error);
return false;
}
}
// Initialize charts with proper configuration
function initCharts() {
const timingCtx = document.getElementById('timingChart').getContext('2d');
const timeSeriesCtx = document.getElementById('timeSeriesChart').getContext('2d');
// Destroy existing charts if they exist
if (timingChart) timingChart.destroy();
if (timeSeriesChart) timeSeriesChart.destroy();
timingChart = new Chart(timingCtx, {
type: 'scatter',
data: { datasets: [] },
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'linear',
title: { display: true, text: 'Click Number' }
},
y: {
type: 'linear',
title: { display: true, text: 'Delay (ms)' }
}
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
return `Click ${context.raw.x}: ${context.raw.y}ms`;
}
}
}
}
}
});
timeSeriesChart = new Chart(timeSeriesCtx, {
type: 'line',
data: { datasets: [] },
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'linear',
title: { display: true, text: 'Click Number' }
},
y: {
type: 'linear',
title: { display: true, text: 'Delay (ms)' }
}
}
}
});
}
// Load available log files from server
async function loadLogFiles() {
try {
fileList.innerHTML = '<div class="loading">Loading log files...</div>';
logFileSelect.innerHTML = '<option value="">-- Loading... --</option>';
const response = await fetch(`${API_BASE}/logs`);
if (!response.ok) {
throw new Error(`Server error: ${response.status}`);
}
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
const files = data.logs;
if (files.length === 0) {
fileList.innerHTML = '<div class="error">No log files found. Run the enhanced autoclicker first.</div>';
logFileSelect.innerHTML = '<option value="">-- No log files available --</option>';
return;
}
// Populate select dropdown
logFileSelect.innerHTML = '<option value="">-- Select a log file --</option>';
files.forEach(file => {
const option = document.createElement('option');
option.value = file.name;
option.textContent = `${file.name} (${file.date})`;
logFileSelect.appendChild(option);
});
// Populate file list
fileList.innerHTML = files.map(file => `
<div class="file-item" data-file="${file.name}">
<div class="file-name">${file.name}</div>
<div class="file-info">Size: ${file.size} | Created: ${file.date}</div>
</div>
`).join('');
// Add click handlers to file items
document.querySelectorAll('.file-item').forEach(item => {
item.addEventListener('click', function() {
document.querySelectorAll('.file-item').forEach(i => i.classList.remove('active'));
this.classList.add('active');
logFileSelect.value = this.dataset.file;
loadBtn.disabled = false;
});
});
} catch (error) {
console.error('Error loading log files:', error);
fileList.innerHTML = `<div class="error">Error loading log files: ${error.message}</div>`;
logFileSelect.innerHTML = '<option value="">-- Error loading files --</option>';
}
}
// Load and parse JSON data from server
async function loadData() {
const fileName = logFileSelect.value;
if (!fileName) return;
currentFile = fileName;
loadBtn.disabled = true;
loadBtn.textContent = 'Loading...';
try {
const response = await fetch(`${API_BASE}/log/${encodeURIComponent(fileName)}`);
if (!response.ok) {
throw new Error(`Server error: ${response.status}`);
}
const data = await response.json();
if (data.error) {
if (data.content_preview) {
fileList.innerHTML = `<div class="error">Invalid JSON in ${fileName}: ${data.error}<br><br>File content preview:<pre>${data.content_preview}</pre></div>`;
} else {
fileList.innerHTML = `<div class="error">Error loading ${fileName}: ${data.error}</div>`;
}
return;
}
if (!data.valid) {
fileList.innerHTML = `<div class="error">File ${fileName} is not valid JSON: ${data.error || 'Unknown error'}</div>`;
return;
}
clickData = data.data;
updateCharts();
updateStats();
statsContainer.style.display = 'flex';
} catch (error) {
console.error('Error loading data:', error);
fileList.innerHTML = `<div class="error">Error loading ${fileName}: ${error.message}</div>`;
} finally {
loadBtn.disabled = false;
loadBtn.textContent = 'Load Data';
}
}
// Update charts with current data
function updateCharts() {
if (clickData.length === 0) return;
// Update timing chart based on selected type
const chartType = chartTypeSelect.value;
if (chartType === 'scatter' || chartType === 'line') {
const dataPoints = clickData.map(item => ({
x: item.click,
y: item.delay_ms
}));
timingChart.data.datasets = [{
label: 'Click Delays',
data: dataPoints,
type: chartType,
backgroundColor: 'rgba(52, 152, 219, 0.5)',
borderColor: 'rgba(52, 152, 219, 1)',
pointRadius: chartType === 'scatter' ? 4 : 2,
tension: chartType === 'line' ? 0.3 : 0,
pointStyle: 'circle'
}];
timingChart.update();
} else if (chartType === 'histogram') {
// Create histogram data
const delays = clickData.map(item => item.delay_ms);
const bins = createHistogram(delays, 20);
const histogramData = bins.map((count, i) => ({
x: i * (700 / 20) + 50,
y: count
}));
timingChart.data.datasets = [{
label: 'Delay Distribution',
data: histogramData,
type: 'bar',
backgroundColor: 'rgba(52, 152, 219, 0.7)',
borderColor: 'rgba(52, 152, 219, 1)',
barPercentage: 0.9
}];
timingChart.options.scales.x.title.text = 'Delay Range (ms)';
timingChart.options.scales.y.title.text = 'Frequency';
timingChart.update();
}
// Update time series chart (using click number instead of time for simplicity)
const timeSeriesData = clickData.map(item => ({
x: item.click,
y: item.delay_ms
}));
timeSeriesChart.data.datasets = [{
label: 'Delay Over Time',
data: timeSeriesData,
borderColor: 'rgba(46, 204, 113, 1)',
backgroundColor: 'rgba(46, 204, 113, 0.1)',
tension: 0.3,
pointRadius: 2,
pointStyle: 'circle'
}];
timeSeriesChart.update();
}
// Create histogram from data
function createHistogram(data, binCount) {
if (data.length === 0) return [];
const min = Math.min(...data);
const max = Math.max(...data);
const binSize = (max - min) / binCount;
const bins = new Array(binCount).fill(0);
data.forEach(value => {
const binIndex = Math.min(Math.floor((value - min) / binSize), binCount - 1);
bins[binIndex]++;
});
return bins;
}
// Update statistics
function updateStats() {
if (clickData.length === 0) return;
const delays = clickData.map(item => item.delay_ms);
const total = delays.length;
const sum = delays.reduce((a, b) => a + b, 0);
const avg = sum / total;
const min = Math.min(...delays);
const max = Math.max(...delays);
// Calculate standard deviation
const variance = delays.reduce((sq, n) => sq + Math.pow(n - avg, 2), 0) / total;
const stdDev = Math.sqrt(variance);
document.getElementById('totalClicks').textContent = total;
document.getElementById('avgDelay').textContent = `${avg.toFixed(1)} ms`;
document.getElementById('minDelay').textContent = `${min} ms`;
document.getElementById('maxDelay').textContent = `${max} ms`;
document.getElementById('stdDev').textContent = `${stdDev.toFixed(1)} ms`;
}
// Event listeners
logFileSelect.addEventListener('change', function() {
loadBtn.disabled = !this.value;
});
chartTypeSelect.addEventListener('change', updateCharts);
loadBtn.addEventListener('click', loadData);
// Initialize
initCharts();
// Test API connection first
testAPIConnection().then(isConnected => {
if (isConnected) {
loadLogFiles();
// Auto-refresh log list every 10 seconds
setInterval(loadLogFiles, 10000);
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,183 @@
#!/usr/bin/env python3
"""
Simple web server for autoclicker visualization
Serves the HTML visualizer and provides API endpoints for CSV log files
Converts CSV to JSON for the frontend
"""
import os
import json
import csv
import http.server
import socketserver
from urllib.parse import urlparse, parse_qs
from datetime import datetime
PORT = 8661
LOG_DIR = "/tmp/autoclicker_logs"
class ClickServerHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=".", **kwargs)
def do_GET(self):
# Handle API requests
if self.path.startswith('/api/'):
self.handle_api_request()
return
# Serve the visualizer HTML for root path
if self.path == '/' or self.path == '/index.html':
self.path = '/click-visualizer.html'
# Serve static files normally
return super().do_GET()
def handle_api_request(self):
"""Handle API endpoints for log file operations"""
try:
if self.path.startswith('/api/logs'):
self.handle_logs_request()
elif self.path.startswith('/api/log/'):
self.handle_log_file_request()
else:
self.send_error(404, "API endpoint not found")
except Exception as e:
self.send_error(500, f"Server error: {str(e)}")
def handle_logs_request(self):
"""Return list of available log files"""
if not os.path.exists(LOG_DIR):
self.send_json_response({"error": "Log directory not found", "logs": []})
return
try:
files = []
for filename in sorted(os.listdir(LOG_DIR), reverse=True):
if filename.endswith('.csv'):
filepath = os.path.join(LOG_DIR, filename)
stat = os.stat(filepath)
# Check if CSV is valid
if self.is_valid_csv(filepath):
files.append({
"name": filename,
"size": self.format_file_size(stat.st_size),
"date": datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
"path": filename,
"valid": True
})
else:
files.append({
"name": filename,
"size": self.format_file_size(stat.st_size),
"date": datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
"path": filename,
"valid": False,
"error": "Invalid CSV format"
})
self.send_json_response({"logs": files})
except Exception as e:
self.send_json_response({"error": str(e), "logs": []})
def handle_log_file_request(self):
"""Return contents of a specific log file as JSON"""
# Extract filename from path
parts = self.path.split('/')
if len(parts) < 4:
self.send_error(400, "Invalid log file request")
return
filename = parts[3]
filepath = os.path.join(LOG_DIR, filename)
if not os.path.exists(filepath):
self.send_error(404, "Log file not found")
return
try:
if filename.endswith('.csv'):
data = self.csv_to_json(filepath)
if data is None:
self.send_json_response({
"error": "Failed to parse CSV file",
"filename": filename,
"valid": False
})
return
self.send_json_response({
"data": data,
"filename": filename,
"valid": True
})
else:
self.send_error(400, "Unsupported file format")
except Exception as e:
self.send_error(500, f"Error reading log file: {str(e)}")
def csv_to_json(self, filepath):
"""Convert CSV file to JSON array"""
try:
data = []
with open(filepath, 'r') as f:
reader = csv.DictReader(f)
for row in reader:
data.append({
"click": int(row["click_number"]),
"delay_ms": int(row["delay_ms"]),
"timestamp": int(row["timestamp"])
})
return data
except Exception as e:
print(f"Error parsing CSV {filepath}: {str(e)}")
return None
def is_valid_csv(self, filepath):
"""Check if a file contains valid CSV"""
try:
with open(filepath, 'r') as f:
reader = csv.DictReader(f)
# Try to read first row
next(reader)
return True
except Exception:
return False
def send_json_response(self, data):
"""Send JSON response with proper headers"""
response = json.dumps(data, indent=2)
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Content-Length', str(len(response)))
self.end_headers()
self.wfile.write(response.encode())
def format_file_size(self, size_bytes):
"""Format file size in human-readable format"""
for unit in ['B', 'KB', 'MB', 'GB']:
if size_bytes < 1024.0:
return f"{size_bytes:.1f} {unit}"
size_bytes /= 1024.0
return f"{size_bytes:.1f} TB"
def run_server():
"""Start the web server"""
print(f"Starting autoclicker visualization server on port {PORT}")
print(f"Log directory: {LOG_DIR}")
print("Open your browser to: http://localhost:8661")
print("Press Ctrl+C to stop the server")
try:
with socketserver.TCPServer(("", PORT), ClickServerHandler) as httpd:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nServer stopped")
except Exception as e:
print(f"Server error: {str(e)}")
if __name__ == "__main__":
run_server()

View File

@@ -0,0 +1,71 @@
#!/bin/bash
# Script to visualize autoclicker log files
# Starts Python server and opens the HTML visualization tool
VISUALIZER_FILE="click-visualizer.html"
LOG_DIR="/tmp/autoclicker_logs"
SERVER_SCRIPT="click_server.py"
SERVER_PORT=8661
# Check if visualization file exists
if [ ! -f "$VISUALIZER_FILE" ]; then
echo "Error: Visualization tool not found at $VISUALIZER_FILE"
exit 1
fi
# Check if server script exists
if [ ! -f "$SERVER_SCRIPT" ]; then
echo "Error: Server script not found at $SERVER_SCRIPT"
exit 1
fi
# Check if Python is available
if ! command -v python3 &> /dev/null; then
echo "Error: Python 3 is not installed. Install it with: sudo pacman -S python"
exit 1
fi
# Check if we have any log files
if [ ! -d "$LOG_DIR" ] || [ -z "$(ls -A "$LOG_DIR")" ]; then
echo "No autoclicker log files found in $LOG_DIR"
echo "Run the enhanced autoclicker first to generate data."
echo ""
echo "Starting server anyway (you can generate logs while it's running)..."
fi
echo "Starting autoclicker visualization server..."
echo "Server will be available at: http://localhost:$SERVER_PORT"
echo "Log directory: $LOG_DIR"
echo ""
# Start the Python server in the background
python3 "$SERVER_SCRIPT" &
SERVER_PID=$!
# Give the server a moment to start
sleep 2
# Check if server started successfully
if ! ps -p $SERVER_PID > /dev/null; then
echo "Error: Failed to start server"
exit 1
fi
# Open browser
if command -v xdg-open &> /dev/null; then
xdg-open "http://localhost:$SERVER_PORT"
elif command -v open &> /dev/null; then
open "http://localhost:$SERVER_PORT"
else
echo "Server started successfully!"
echo "Open your browser and navigate to: http://localhost:$SERVER_PORT"
fi
echo ""
echo "Server is running in the background (PID: $SERVER_PID)"
echo "Press Ctrl+C to stop the server when you're done"
echo ""
# Wait for server to finish (it will run until manually stopped)
wait $SERVER_PID

123
scripts/osrs/mouse-toggle.sh Executable file
View File

@@ -0,0 +1,123 @@
#!/bin/bash
# Mouse toggle script for Sway
# Toggles mouse position 60 pixels left/right on each execution
# Requires ydotool and ydotoold daemon running
# Configuration
### 1T KARAMBWANS
MOVE_DISTANCE=120
#RANDOM_NUM=$((RANDOM % 101)) # Generate 0-100
#RANDOM_DELAY=$(echo "scale=3; 0.5 + $RANDOM_NUM / 1000" | bc)
RANDOM_DELAY=0.575
RANDOM_DELAY_LEFT=0.05
### ANTIVENOM POTIONS
#MOVE_DISTANCE=50
#RANDOM_DELAY=0.6
#RANDOM_DELAY_LEFT=0.6
### DRAGON JAVELINS
#MOVE_DISTANCE=50
#RANDOM_DELAY=0.1
#RANDOM_DELAY_LEFT=0.3
### PRAYER BONES
MOVE_DISTANCE=120
#RANDOM_NUM=$((RANDOM % 101)) # Generate 0-100
#RANDOM_DELAY=$(echo "scale=3; 0.5 + $RANDOM_NUM / 1000" | bc)
RANDOM_DELAY=0.100
RANDOM_DELAY_LEFT=0.05
STATE_FILE="/tmp/mouse_toggle_state"
TIMESTAMP_FILE="/tmp/mouse_toggle_timestamp"
ENABLED_FILE="/tmp/mouse_toggle_enabled"
LOCK_FILE="/tmp/mouse_toggle_lock"
RESET_TIMEOUT=1 # Reset to right after 1 second
# Check if ydotool is available
if ! command -v ydotool &> /dev/null; then
echo "Error: ydotool is not installed. Install it with: sudo pacman -S ydotool"
exit 1
fi
# Check if bc is available (needed for random delay calculation)
if ! command -v bc &> /dev/null; then
echo "Error: bc is not installed. Install it with: sudo pacman -S bc"
exit 1
fi
# Check if script is already running (prevent multiple instances)
if [ -f "$LOCK_FILE" ]; then
echo "Script is already running, skipping..."
exit 0
fi
# Create lock file
echo $$ > "$LOCK_FILE"
# Clean up lock file on exit
trap 'rm -f "$LOCK_FILE"' EXIT
# Check if script should toggle on/off or execute
if [ "$1" = "toggle" ]; then
if [ -f "$ENABLED_FILE" ]; then
rm "$ENABLED_FILE"
echo "Mouse toggle script DISABLED"
else
echo "enabled" > "$ENABLED_FILE"
echo "Mouse toggle script ENABLED"
fi
exit 0
fi
# Check if script is enabled
if [ ! -f "$ENABLED_FILE" ]; then
echo "Mouse toggle script is DISABLED. Use '$0 toggle' to enable."
exit 0
fi
# Check if ydotoold daemon is running
if ! pgrep -x "ydotoold" > /dev/null; then
echo "Starting ydotoold daemon..."
sudo ydotoold &
sleep 1
fi
# Get current timestamp
CURRENT_TIME=$(date +%s)
# Read current state and timestamp
if [ -f "$STATE_FILE" ] && [ -f "$TIMESTAMP_FILE" ]; then
LAST_ACTION=$(cat "$STATE_FILE")
LAST_TIME=$(cat "$TIMESTAMP_FILE")
TIME_DIFF=$((CURRENT_TIME - LAST_TIME))
# If more than RESET_TIMEOUT seconds have passed, reset to "right"
if [ $TIME_DIFF -gt $RESET_TIMEOUT ]; then
echo "More than ${RESET_TIMEOUT}s since last action, resetting to move right first"
LAST_ACTION="left" # Set to left so next action will be right
fi
else
LAST_ACTION="left" # Default to left so first action is right
fi
# Toggle the action and move mouse
if [ "$LAST_ACTION" = "right" ]; then
# Last action was right, so move left
echo "Moving mouse left ($MOVE_DISTANCE pixels) and clicking (delay: ${RANDOM_DELAY}s)"
ydotool mousemove -- -$MOVE_DISTANCE 0
sleep $RANDOM_DELAY # Random delay between 0.3-0.5s
ydotool click 0xC0 # Left click
echo "left" > "$STATE_FILE"
echo "$CURRENT_TIME" > "$TIMESTAMP_FILE"
else
# Last action was left (or unknown), so move right
echo "Moving mouse right ($MOVE_DISTANCE pixels) and clicking (delay: ${RANDOM_DELAY}s)"
ydotool mousemove -- $MOVE_DISTANCE 0
sleep $RANDOM_DELAY_LEFT # Random delay between 0.1-0.2s
ydotool click 0xC0 # Left click
echo "right" > "$STATE_FILE"
echo "$CURRENT_TIME" > "$TIMESTAMP_FILE"
fi

3
scripts/osrs/osrs-click.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
ydotool click 0xC0 # Left click

93
scripts/osrs/osrs-drop.sh Executable file
View File

@@ -0,0 +1,93 @@
#!/bin/bash
# Mouse toggle script for Sway
# Toggles mouse position 60 (MOVE_DISTANCE) pixels upwards on each execution
# Requires ydotool and ydotoold daemon running
# Configuration
MOVE_DISTANCE=70
BOTTOM_MARGIN=190
RANDOM_DELAY_MIN=0
RANDOM_DELAY_MAX=0.25
RANDOM_DELAY=$(awk -v min=$RANDOM_DELAY_MIN -v max=$RANDOM_DELAY_MAX 'BEGIN { srand(); printf "%.2f\n", min + rand() * (max - min) }')
#WL_FIND_CURSOR_BIN="${WL_FIND_CURSOR_BIN:-wl-find-cursor}"
WL_FIND_CURSOR_BIN="/home/raga/repos/linux/wl-find-cursor/wl-find-cursor"
ENABLED_FILE="/tmp/osrs_drop__enabled"
LOCK_FILE="/tmp/osrs_drop__lock"
# Verify dependencies
if ! command -v ydotool &> /dev/null; then
echo "Error: ydotool is not installed. Install it with: sudo pacman -S ydotool"
exit 1
fi
if ! command -v bc &> /dev/null; then
echo "Error: bc is not installed. Install it with: sudo pacman -S bc"
exit 1
fi
if ! command -v jq &> /dev/null; then
echo "Error: jq is not installed. Install it with: sudo pacman -S jq"
exit 1
fi
if ! command -v "$WL_FIND_CURSOR_BIN" &> /dev/null; then
echo "Error: wl-find-cursor is not installed or not in PATH (looked for '$WL_FIND_CURSOR_BIN')."
exit 1
fi
# Verify lock file
if [ -f "$LOCK_FILE" ]; then
echo "Script is already running, skipping..."
exit 0
fi
echo $$ > "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"' EXIT
# Check if script should toggle on/off or execute
if [ "$1" = "toggle" ]; then
if [ -f "$ENABLED_FILE" ]; then
rm "$ENABLED_FILE"
echo "Mouse toggle script DISABLED"
else
echo "enabled" > "$ENABLED_FILE"
echo "Mouse toggle script ENABLED"
fi
exit 0
fi
# Verify script is enabled
if [ ! -f "$ENABLED_FILE" ]; then
echo "Mouse toggle script is DISABLED. Use '$0 toggle' to enable."
exit 0
fi
# Verify ydotoold daemon is running
if ! pgrep -x "ydotoold" > /dev/null; then
echo "Starting ydotoold daemon..."
sudo ydotoold &
sleep 1
fi
CURSOR_POS=$("$WL_FIND_CURSOR_BIN" -p)
if [ -z "$CURSOR_POS" ]; then
echo "Error: Unable to determine cursor position."
exit 1
fi
read -r CURSOR_X CURSOR_Y <<< "$CURSOR_POS"
BOTTOM_LIMIT=$(swaymsg -r -t get_outputs | jq --argjson cx "$CURSOR_X" --argjson cy "$CURSOR_Y" --argjson margin "$BOTTOM_MARGIN" '
.[] | select(.active == true) |
select($cx >= .rect.x and $cx < (.rect.x + .rect.width) and
$cy >= .rect.y and $cy < (.rect.y + .rect.height)) |
(.rect.y + .rect.height - $margin)
' | head -n 1)
if [ -n "$BOTTOM_LIMIT" ]; then
if [ "$(printf "%.0f" "$CURSOR_Y")" -ge "$(printf "%.0f" "$BOTTOM_LIMIT")" ]; then
echo "Cursor is already within ${BOTTOM_MARGIN}px of the bottom, skipping move."
exit 0
fi
fi
ydotool mousemove -- 0 $MOVE_DISTANCE

File diff suppressed because it is too large Load Diff