diff --git a/I38.md b/I38.md index 72c29de..24dd026 100644 --- a/I38.md +++ b/I38.md @@ -84,6 +84,39 @@ Bypass mode passes all keys directly to the application, which is useful for app *GNOME/MATE comparison:* In GNOME/MATE, applications always receive keyboard input directly. Bypass mode simulates this behavior within i3. +## Personal Mode + +Personal Mode is a user-defined mode for adding your own keybindings that will not be overwritten when you regenerate your I38 configuration. + +### Entering Personal Mode + +When you first run I38, you are prompted to choose a personal mode key. If you selected one, press that key to enter Personal Mode. + +### Adding Custom Bindings + +To add your own shortcuts, edit the file `~/.config/i3/customizations`. This file is never overwritten by I38. + +Inside the `mode "personal" { ... }` block, you can add any i3 `bindsym` commands. Be sure to end each binding with `, mode "default"` so that Personal Mode exits after the action. + +Example: + +```i3 +mode "personal" { + bindsym f exec firefox, mode "default" + bindsym Shift+f exec firefox --private-window, mode "default" + bindsym Escape mode "default" + bindsym Control+g mode "default" +} +``` + +In this example, pressing your personal mode key followed by `f` launches Firefox, and `Shift+f` launches a private Firefox window. + +### Default Personal Mode Bindings + +By default, Personal Mode includes: +- `F1` | Show personal mode keybindings +- `Escape` or `Control` + `g` | Exit Personal Mode without taking action + ## Panel Mode Panel Mode provides quick access to information displays and utility panels. To enter Panel Mode, press `Alt` + `Control` + `Tab`. A distinctive sound will play when Panel Mode is active. @@ -100,6 +133,10 @@ In Panel Mode, single keypresses launch different information panels: | `r` | Open reminder panel | | `n` | Launch notes application | | `b` | Open bluetooth. *requires blueman be installed at the time of your i3 config generation* | +| `Shift` + `b` | Show detailed battery information | +| `m` | Launch password manager | +| `p` | Show power options | +| `Control` + `MODKEY` + `l` | Lock screen *(if screen lock PIN is configured)* | | `Escape` or `Control` + `g` | Exit Panel Mode without taking action | Just like Ratpoison Mode, Panel Mode automatically returns you to Default Mode after a selection is made or when you press Escape/Control+g to cancel. diff --git a/README.md b/README.md index f46184e..56270a3 100644 --- a/README.md +++ b/README.md @@ -187,124 +187,35 @@ The AI assistant stores its configuration in `~/.config/stormux/I38/ai.conf` usi **Ollama**: Requires Ollama service to be running locally. Automatically discovers available models and supports text analysis. -## Custom Applications in Ratpoison Mode +## Personal Mode -I38 now includes a powerful system for adding custom applications to ratpoison mode with user-defined keybindings. During the configuration process, after setting up window event sounds, you'll be prompted to add custom applications that will be accessible through ratpoison mode. +Personal Mode is a user-defined mode where you can add your own keybindings without worrying about them being overwritten when you update I38. -### How It Works +### Setup -The system will prompt you through a series of questions for each application you want to add: +When you run `i38.sh`, you are prompted to choose a personal mode key (or you can skip it). If you selected one, pressing that key enters Personal Mode. -1. **Application Name**: A descriptive name for the application -2. **Execution Command**: The full path or command to run the application -3. **Command Flags**: Optional command-line arguments -4. **Keybinding**: A custom key combination using I38's notation system +### Adding Custom Bindings -### Keybinding Notation System +Edit `~/.config/i3/customizations` to add your own shortcuts. This file is never overwritten by I38. Inside the `mode "personal" { ... }` block, add i3 `bindsym` commands and end each one with `, mode "default"` so the mode exits after the action. -I38 uses a simple notation system to define keybindings that supports modifiers and special keys: +Example: -#### Modifiers -- `^` = Control (Ctrl) -- `!` = Alt -- `#` = Super (Windows/Cmd key) -- `m` = Your chosen mod key (configured during setup) -- Uppercase letters automatically add Shift (e.g., `C` = Shift+c) - -#### Special Keys -- **Function Keys**: `f1`, `f2`, ... `f12` -- **Arrow Keys**: `up`, `down`, `left`, `right` -- **Navigation Keys**: `home`, `end`, `pageup`, `pagedown` -- **Editing Keys**: `insert`, `delete`, `backspace` -- **Other Keys**: `space`, `tab`, `return`, `escape`, `print` - -#### Example Keybindings -- `c` = Just the 'c' key -- `^c` = Control+c -- `!f1` = Alt+F1 -- `mspace` = Your mod key + Space -- `^!up` = Control+Alt+Up arrow -- `#pagedown` = Super+Page Down -- `C` = Shift+c -- `^C` = Control+Shift+c - -### Example Configuration Session - -Here's what the configuration prompts look like: - -``` -Enter application name (or press enter when finished): Discord -Enter execution path/command for Discord: discord -Enter command line flags for Discord (optional): --no-sandbox -Enter keybinding for Discord (Examples: c, ^c, !f1, mspace, ^!up) or ? for help: d +```i3 +mode "personal" { + bindsym f exec firefox, mode "default" + bindsym Shift+f exec firefox --private-window, mode "default" + bindsym Escape mode "default" + bindsym Control+g mode "default" +} ``` -Another example: -``` -Enter application name (or press enter when finished): Terminal Calculator -Enter execution path/command for Terminal Calculator: gnome-calculator -Enter command line flags for Terminal Calculator (optional): -Enter keybinding for Terminal Calculator (Examples: c, ^c, !f1, mspace, ^!up) or ? for help: ^#c -``` +With this example, pressing your personal mode key followed by `f` opens Firefox. `Shift+f` opens a private window. -### Getting Help During Configuration - -If you type `?` when prompted for a keybinding, I38 will display a comprehensive help screen with: -- Complete modifier notation reference -- List of all supported special keys -- Multiple examples of different keybinding combinations -- Rules about uppercase letters and Shift - -### Conflict Detection - -I38 automatically prevents keybinding conflicts by: -- Maintaining a list of all existing ratpoison mode bindings -- Checking your custom keybinding against reserved keys -- Showing an error message if you try to use a taken keybinding -- Asking you to choose a different key combination - -### Reserved Keybindings - -The following keys are already used in ratpoison mode and cannot be assigned to custom applications: - -- `c` = Terminal -- `e` = Text editor -- `f` = File manager -- `w` = Web browser -- `k` = Kill window -- `m` = Mumble (if installed) -- `p` = Pidgin (if installed) -- `g` = Game controller status -- Various modifier combinations for volume, music controls, etc. - -### Using Your Custom Applications - -Once configured, access your custom applications by: -1. Press your ratpoison mode key (e.g., Alt+Escape) -2. Press your custom keybinding (e.g., `d` for Discord) -3. The application launches and ratpoison mode exits - -### Advanced Examples - -**Media Applications:** -- `vlc` bound to `v` for VLC media player -- `spotify` bound to `^s` for Spotify with Control+s - -**Development Tools:** -- `code` bound to `mf2` for VS Code with mod+F2 -- `gimp` bound to `!g` for GIMP with Alt+g - -**System Tools:** -- `htop` bound to `^#h` for system monitor with Control+Super+h -- `pavucontrol` bound to `!space` for audio control with Alt+Space - -### Tips for Choosing Keybindings - -1. **Keep it memorable**: Use the first letter of the application name when possible -2. **Group related apps**: Use similar modifiers for related applications -3. **Consider frequency**: Use simpler keys for frequently used applications -4. **Avoid conflicts**: The system will warn you, but plan ahead for a logical layout +### Default Bindings +- `F1` — Show personal mode keybindings +- `Escape` or `Control+g` — Exit Personal Mode ## Panel Mode diff --git a/i38.sh b/i38.sh index 96c2b95..3b1b29b 100755 --- a/i38.sh +++ b/i38.sh @@ -11,8 +11,6 @@ # PARTICULAR PURPOSE. See the GNU General Public License for more details. # You should have received a copy of the GNU General Public License along with this program. If not, see . -# Flag for sway configurations -usingSway=1 # Not by default. i3Path="${XDG_CONFIG_HOME:-$HOME/.config}/i3" i3msg="i3-msg" configFile="${PWD}/I38_preferences.conf" @@ -527,7 +525,6 @@ EOF # Array of command line arguments declare -A command=( [h]="This help screen." - [s]="Create sway configuration instead of i3." [u]="Copy over the latest version of scripts." [x]="Generate ~/.xinitrc and ~/.xprofile." [X]="Generate ~/.xprofile only." @@ -539,12 +536,6 @@ args="${args//[[:space:]]/}" while getopts "${args}" i ; do case "$i" in h) help;; - s) - swaySystemIncludesPath="/etc/sway/config.d" - usingSway=0 - i3msg="swaymsg" - i3Path="${XDG_CONFIG_HOME:-$HOME/.config}/sway" - ;; u) update_scripts;; x) write_xinitrc ;& X) write_xprofile ;; @@ -879,15 +870,6 @@ cat << EOF > "${i3Path}/config" # $(date '+%A, %B %d, %Y at %I:%M%p') EOF -# If we are using Sway, we need to load in the system configuration -# Usually, this is for system specific dBus things that the distro knows how to manage; we should trust their judgment with that -if [[ $usingSway ]] && [[ -d "${swaySystemIncludesPath}" ]]; then - cat << EOF >> "${i3Path}/config" -# Include your distribution Sway configuration files. -include ${swaySystemIncludesPath}/* -EOF -fi - cat << EOF >> "${i3Path}/config" # i3 config file (v4) # @@ -1211,16 +1193,10 @@ bindsym Shift+c exec $(command -v cthulhu) --replace, mode "default" bindsym Shift+o exec $(command -v orca) --replace, mode "default" # Toggle screen reader bindsym Shift+t exec ${i3Path}/scripts/toggle_screenreader.sh, mode "default" -$(if [[ $usingSway -eq 0 ]]; then - echo "# reload the configuration file" - echo "bindsym Control+semicolon exec bash -c '$i3msg -t command reload && spd-say -P important -Cw \"I38 Configuration reloaded.\"', mode \"default\"" - -else - echo "# reload the configuration file" - echo "bindsym Control+semicolon exec bash -c '$i3msg -t run_command reload && spd-say -P important -Cw \"I38 Configuration reloaded.\"', mode \"default\"" - echo "# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)" - echo "bindsym Control+Shift+semicolon exec $i3msg -t run_command restart && spd-say -P important -Cw \"I3 restarted.\", mode \"default\"" -fi) +# reload the configuration file +bindsym Control+semicolon exec bash -c '$i3msg -t run_command reload && spd-say -P important -Cw "I38 Configuration reloaded."', mode "default" +# restart i3 inplace (preserves your layout/session, can be used to upgrade i3) +bindsym Control+Shift+semicolon exec $i3msg -t run_command restart && spd-say -P important -Cw "I3 restarted.", mode "default" # Run dialog with exclamation bindsym Shift+exclam exec ${i3Path}/scripts/run_dialog.sh, mode "default" # exit i3 (logs you out of your X session) @@ -1239,50 +1215,28 @@ fi cat << EOF >> "${i3Path}/config" # Auto start section $(if [[ $sounds -eq 0 ]]; then - if [[ $usingSway -eq 0 ]]; then - echo "exec --no-startup-id ${i3Path}/scripts/sound.py" - else - echo "exec_always --no-startup-id ${i3Path}/scripts/sound.py" - fi + echo "exec_always --no-startup-id ${i3Path}/scripts/sound.py" fi -# Steam game focus handler (i3 only) - focuses games when they open behind Big Picture -if [[ $usingSway -ne 0 ]] && command -v steam &> /dev/null; then +# Steam game focus handler - focuses games when they open behind Big Picture +if command -v steam &> /dev/null; then echo "exec --no-startup-id ${i3Path}/scripts/steam_games.py" fi # i3 watchdog - monitors i3 responsiveness and auto-recovers from freezes -if [[ $usingSway -ne 0 ]]; then - echo "exec_always --no-startup-id ${i3Path}/scripts/i3_watchdog.sh" -fi -# xbrlapi is X11-only, skip on Sway/Wayland -if [[ $brlapi -eq 0 ]] && [[ $usingSway -ne 0 ]]; then +echo "exec_always --no-startup-id ${i3Path}/scripts/i3_watchdog.sh" +if [[ $brlapi -eq 0 ]]; then echo 'exec --no-startup-id xbrlapi --quiet' fi if [[ $udiskie -eq 0 ]]; then echo 'exec --no-startup-id udiskie' fi # Notification daemon -if [[ $usingSway -eq 0 ]]; then - # Sway: prefer Wayland-native notification daemons - if command -v mako &> /dev/null; then - echo 'exec_always --no-startup-id mako' - elif [[ -x "/usr/lib/xfce4/notifyd/xfce4-notifyd" ]]; then - # Fallback to X11 variant via XWayland - echo 'exec_always --no-startup-id /usr/lib/xfce4/notifyd/xfce4-notifyd' - elif [[ -x "/usr/lib/notification-daemon-1.0/notification-daemon" ]]; then - echo 'exec_always --no-startup-id /usr/lib/notification-daemon-1.0/notification-daemon -r' - elif [[ -x "/usr/libexec/notification-daemon" ]]; then - echo 'exec_always --no-startup-id /usr/libexec/notification-daemon -r' - fi -else - # i3: use X11 notification daemons - if [[ -x "/usr/lib/xfce4/notifyd/xfce4-notifyd" ]]; then - echo 'exec_always --no-startup-id /usr/lib/xfce4/notifyd/xfce4-notifyd' - elif [[ -x "/usr/lib/notification-daemon-1.0/notification-daemon" ]]; then - echo 'exec_always --no-startup-id /usr/lib/notification-daemon-1.0/notification-daemon -r' - elif [[ -x "/usr/libexec/notification-daemon" ]]; then - # Work around for weird Void Linux stuff - echo 'exec_always --no-startup-id /usr/libexec/notification-daemon -r' - fi +if [[ -x "/usr/lib/xfce4/notifyd/xfce4-notifyd" ]]; then + echo 'exec_always --no-startup-id /usr/lib/xfce4/notifyd/xfce4-notifyd' +elif [[ -x "/usr/lib/notification-daemon-1.0/notification-daemon" ]]; then + echo 'exec_always --no-startup-id /usr/lib/notification-daemon-1.0/notification-daemon -r' +elif [[ -x "/usr/libexec/notification-daemon" ]]; then + # Work around for weird Void Linux stuff + echo 'exec_always --no-startup-id /usr/libexec/notification-daemon -r' fi if command -v remind &> /dev/null && command -v notify-send &> /dev/null ; then echo "exec_always --no-startup-id ${i3Path}/scripts/launch_remind.sh" @@ -1291,39 +1245,25 @@ fi if [[ $batteryAlert -eq 0 ]]; then echo "exec_always --no-startup-id ${i3Path}/scripts/battery_alert.sh" fi -if [[ $usingSway -ne 0 ]] && [[ -n "$screenlockPinHash" ]] && [[ "${screenlockAutolockSeconds:-0}" -gt 0 ]] && command -v xprintidle &> /dev/null; then +if [[ -n "$screenlockPinHash" ]] && [[ "${screenlockAutolockSeconds:-0}" -gt 0 ]] && command -v xprintidle &> /dev/null; then echo "exec_always --no-startup-id ${i3Path}/scripts/screenlock_autolock.sh ${screenlockAutolockSeconds}" fi # WayTray system tray daemon if command -v waytray-daemon &> /dev/null ; then echo 'exec_always --no-startup-id bash -c "pgrep -x waytray-daemon > /dev/null || waytray-daemon"' fi +echo "exec_always --no-startup-id ${i3Path}/scripts/i38-clipboard.py --daemon" +echo "exec_always --no-startup-id ${i3Path}/scripts/desktop.sh" if [[ $dex -eq 0 ]]; then echo '# Start XDG autostart .desktop files using dex. See also' echo '# https://wiki.archlinux.org/index.php/XDG_Autostart' - if [[ $usingSway -eq 0 ]]; then - echo 'exec --no-startup-id dex --autostart --environment sway' - else - echo 'exec --no-startup-id dex --autostart --environment i3' - fi + echo 'exec --no-startup-id dex --autostart --environment i3' else echo '# Startup applications' - # x11bell is X11-only, skip on Sway/Wayland - if command -v x11bell &> /dev/null && [[ $usingSway -ne 0 ]]; then + if command -v x11bell &> /dev/null; then echo 'exec --no-startup-id x11bell play -nqV0 synth .1 sq norm -12' fi - # Clipboard manager - if [[ $usingSway -eq 0 ]]; then - # Sway: use Wayland clipboard manager - if command -v wl-paste &> /dev/null && command -v clipman &> /dev/null; then - echo 'exec wl-paste -t text --watch clipman store' - fi - else - # i3: use I38's accessible clipboard history daemon - echo "exec_always --no-startup-id ${i3Path}/scripts/i38-clipboard.py --daemon" - fi echo "exec $screenReader" - echo "exec_always --no-startup-id ${i3Path}/scripts/desktop.sh" fi) # First run help documentation diff --git a/scripts/ai.py b/scripts/ai.py index 72e9a4a..bb5b322 100755 --- a/scripts/ai.py +++ b/scripts/ai.py @@ -54,8 +54,7 @@ class SystemCommands: } optional_commands = { - 'xclip': 'Required for clipboard on X11', - 'wl-paste': 'Required for clipboard on Wayland', + 'xclip': 'Required for clipboard', 'tesseract': 'Required for OCR functionality', } @@ -1607,18 +1606,11 @@ class AiAssistant(Gtk.Window): def analyze_selected_in_thread(): try: # First, try to get clipboard content (selected text) - # Use wl-paste on Wayland, xclip on X11 selected_text = "" - if os.environ.get('WAYLAND_DISPLAY'): - if SystemCommands.is_command_available('wl-paste'): - clipboard_result = subprocess.run(['wl-paste', '-p'], - capture_output=True, text=True, timeout=5) - selected_text = clipboard_result.stdout.strip() if clipboard_result.returncode == 0 else "" - else: - if SystemCommands.is_command_available('xclip'): - clipboard_result = subprocess.run(['xclip', '-o', '-selection', 'primary'], - capture_output=True, text=True, timeout=5) - selected_text = clipboard_result.stdout.strip() if clipboard_result.returncode == 0 else "" + if SystemCommands.is_command_available('xclip'): + clipboard_result = subprocess.run(['xclip', '-o', '-selection', 'primary'], + capture_output=True, text=True, timeout=5) + selected_text = clipboard_result.stdout.strip() if clipboard_result.returncode == 0 else "" if selected_text: # We have selected text, analyze it diff --git a/scripts/announce_workspace.sh b/scripts/announce_workspace.sh index e73ad48..421ade9 100755 --- a/scripts/announce_workspace.sh +++ b/scripts/announce_workspace.sh @@ -11,18 +11,11 @@ # You should have received a copy of the GNU General Public License along with I38. If not, see . -path="$(readlink -f $0)" +path="$(readlink -f "$0")" path="${path%/*/*}" -path="${path##*/}" -if [[ "$path" == "i3" ]]; then - workSpace="$(i3-msg -t get_workspaces \ - | jq '.[] | select(.focused==true).name' \ - | cut -d"\"" -f2)" -else - workSpace="$(swaymsg -t get_workspaces \ - | jq '.[] | select(.focused==true).name' \ - | cut -d"\"" -f2)" -fi +workSpace="$(i3-msg -t get_workspaces \ + | jq '.[] | select(.focused==true).name' \ + | cut -d"\"" -f2)" left=9 right=0 msg="Workspace ${workSpace}" @@ -41,9 +34,9 @@ else right=9 elif [[ ${workSpace} -gt 5 ]]; then right=9 - ((left-=${workSpace})) + ((left-=workSpace)) else - ((right+=${workSpace})) + ((right+=workSpace)) fi fi play -nqV0 synth pi fade 0 .25 .15 pad 0 1 reverb overdrive riaa norm -8 speed 1 remix v0.${left} v0.${right} & diff --git a/scripts/bind_to_scratchpad.sh b/scripts/bind_to_scratchpad.sh index 061985d..d34c405 100755 --- a/scripts/bind_to_scratchpad.sh +++ b/scripts/bind_to_scratchpad.sh @@ -10,53 +10,25 @@ # You should have received a copy of the GNU General Public License along with I38. If not, see . -# Find out if we're using i3 or sway -if ! [[ -n "${WAYLAND_DISPLAY}" ]]; then - cmd="i3-msg" - scratchConfig="${XDG_CONFIG_HOME:-$HOME/.config}/i3" - usingWayland=false -else - cmd="swaymsg" - scratchConfig="${XDG_CONFIG_HOME:-$HOME/.config}/sway" - usingWayland=true -fi +scratchConfig="${XDG_CONFIG_HOME:-$HOME/.config}/i3" scratchConfig+="/scratchpad" touch "${scratchConfig}" # Get the class name of the focused window -if [[ "$usingWayland" == "true" ]]; then - # Wayland/Sway: use swaymsg to get focused window info - class=$($cmd -t get_tree | jq -r '.. | select(.focused? == true) | .app_id // .window_properties.class' | head -n 1) -else - # X11/i3: use xdotool and xprop - if ! command -v xdotool &> /dev/null || ! command -v xprop &> /dev/null; then - notify-send "Error: xdotool and xprop required for i3" - exit 1 - fi - windowId=$(xdotool getactivewindow) - class=$(xprop -id "$windowId" WM_CLASS | awk -F '"' '{print $4}') +if ! command -v xdotool &> /dev/null || ! command -v xprop &> /dev/null; then + notify-send "Error: xdotool and xprop required for i3" + exit 1 fi +windowId=$(xdotool getactivewindow) +class=$(xprop -id "$windowId" WM_CLASS | awk -F '"' '{print $4}') if [[ -z "$class" ]]; then notify-send "Unable to move to scratchpad." exit 1 fi -# Check if it's already in the config -if [[ "$usingWayland" == "true" ]]; then - # Sway uses app_id for Wayland-native apps, class for XWayland apps - if ! grep -q "app_id=\"$class\"" "$scratchConfig" && ! grep -q "class=\"$class\"" "$scratchConfig"; then - echo "for_window [app_id=\"$class\"] move to scratchpad" >> "$scratchConfig" - notify-send "Added window app_id $class to scratchpad" - fi - # Move the window to scratchpad now (try both app_id and class) - $cmd "[app_id=\"$class\"] move to scratchpad" 2>/dev/null || $cmd "[class=\"$class\"] move to scratchpad" -else - # i3 uses class - if ! grep -q "class=\"$class\"" "$scratchConfig"; then - echo "for_window [class=\"$class\"] move to scratchpad" >> "$scratchConfig" - notify-send "Added window class $class to scratchpad" - fi - # Move the window to scratchpad now - $cmd "[class=\"$class\"] move to scratchpad" +if ! grep -q "class=\"$class\"" "$scratchConfig"; then + echo "for_window [class=\"$class\"] move to scratchpad" >> "$scratchConfig" + notify-send "Added window class $class to scratchpad" fi +i3-msg "[class=\"$class\"] move to scratchpad" diff --git a/scripts/desktop.sh b/scripts/desktop.sh index ddd3d59..c4d70a7 100755 --- a/scripts/desktop.sh +++ b/scripts/desktop.sh @@ -20,29 +20,8 @@ trim_whitespace() { get_shortcuts_file() { local configHome="${XDG_CONFIG_HOME:-$HOME/.config}" - local candidatePath= + local candidatePath="${configHome}/i3/desktop_shortcuts" - if [[ -n "${SWAYSOCK:-}" ]]; then - candidatePath="${configHome}/sway/desktop_shortcuts" - if [[ -r "$candidatePath" ]]; then - printf '%s\n' "$candidatePath" - return 0 - fi - else - candidatePath="${configHome}/i3/desktop_shortcuts" - if [[ -r "$candidatePath" ]]; then - printf '%s\n' "$candidatePath" - return 0 - fi - fi - - candidatePath="${configHome}/i3/desktop_shortcuts" - if [[ -r "$candidatePath" ]]; then - printf '%s\n' "$candidatePath" - return 0 - fi - - candidatePath="${configHome}/sway/desktop_shortcuts" if [[ -r "$candidatePath" ]]; then printf '%s\n' "$candidatePath" return 0 @@ -54,7 +33,6 @@ get_shortcuts_file() { build_launch_command() { local shortcutCommand="$1" local shortcutWorkspace="$2" - local wmCommand= local workspaceEscaped= if [[ -z "$shortcutWorkspace" ]]; then @@ -62,28 +40,18 @@ build_launch_command() { return 0 fi - if [[ -n "${SWAYSOCK:-}" ]] && command -v swaymsg &> /dev/null; then - wmCommand="swaymsg" - elif [[ -n "${I3SOCK:-}" ]] && command -v i3-msg &> /dev/null; then - wmCommand="i3-msg" - elif [[ -n "${WAYLAND_DISPLAY:-}" ]] && command -v swaymsg &> /dev/null; then - wmCommand="swaymsg" - elif command -v i3-msg &> /dev/null; then - wmCommand="i3-msg" - fi - - if [[ -z "$wmCommand" ]]; then + if ! command -v i3-msg &> /dev/null; then printf '%s\n' "$shortcutCommand" return 0 fi if [[ "$shortcutWorkspace" =~ ^[0-9]+$ ]]; then - printf '%s workspace number %s && %s\n' "$wmCommand" "$shortcutWorkspace" "$shortcutCommand" + printf 'i3-msg workspace number %s && %s\n' "$shortcutWorkspace" "$shortcutCommand" return 0 fi workspaceEscaped=$(printf '%q' "$shortcutWorkspace") - printf '%s workspace %s && %s\n' "$wmCommand" "$workspaceEscaped" "$shortcutCommand" + printf 'i3-msg workspace %s && %s\n' "$workspaceEscaped" "$shortcutCommand" } desktopPath="${HOME}/Desktop" diff --git a/scripts/keyboard.sh b/scripts/keyboard.sh index c5e577e..1a75151 100755 --- a/scripts/keyboard.sh +++ b/scripts/keyboard.sh @@ -19,58 +19,27 @@ set -e -# Detect if we're on Wayland or X11 -if [[ -n "${WAYLAND_DISPLAY}" ]]; then - usingWayland=true -else - usingWayland=false -fi - get_kbdlayout() { - if [[ "$usingWayland" == "true" ]]; then - # Sway: Get keyboard layout from input devices - # This gets the xkb_active_layout_name from the first keyboard - layout=$(swaymsg -t get_inputs | jq -r '.[] | select(.type=="keyboard") | .xkb_active_layout_name' | head -n 1) - echo "$layout" - else - # i3: Use setxkbmap - layout=$(setxkbmap -query | grep -oP 'layout:\s*\K([\w,]+)') - variant=$(setxkbmap -query | grep -oP 'variant:\s*\K(\w+)') - echo "$layout" "$variant" - fi + layout=$(setxkbmap -query | grep -oP 'layout:\s*\K([\w,]+)') + variant=$(setxkbmap -query | grep -oP 'variant:\s*\K(\w+)') + echo "$layout" "$variant" } set_kbdlayout() { - eval "array=($1)" + read -r -a array <<< "$1" - if [[ "$usingWayland" == "true" ]]; then - # Sway: Switch to next keyboard layout - # Sway cycles through layouts configured in the config, so we just trigger next - swaymsg input type:keyboard xkb_switch_layout next && - spd-say -P important -Cw "${array[@]}" - else - # i3: Use setxkbmap - setxkbmap "${array[@]}" && - spd-say -P important -Cw "${array[@]}" - fi + setxkbmap "${array[@]}" && + spd-say -P important -Cw "${array[@]}" } cycle() { - if [[ "$usingWayland" == "true" ]]; then - # Sway: Just switch to next layout (Sway handles cycling internally) - swaymsg input type:keyboard xkb_switch_layout next - currentLayout=$(get_kbdlayout) - spd-say -P important -Cw "$currentLayout" - else - # i3: Cycle through provided layouts - currentLayout=$(get_kbdlayout | xargs) - layouts=("$@" "$1") # add the first one at the end so that it cycles - index=0 - while [ "${layouts[$index]}" != "$currentLayout" ] && [ $index -lt "${#layouts[@]}" ]; do index=$((index + 1)); done - nextIndex=$((index + 1)) - nextLayout=${layouts[$nextIndex]} - set_kbdlayout "$nextLayout" - fi + currentLayout=$(get_kbdlayout | xargs) + layouts=("$@" "$1") # add the first one at the end so that it cycles + index=0 + while [ "${layouts[$index]}" != "$currentLayout" ] && [ $index -lt "${#layouts[@]}" ]; do index=$((index + 1)); done + nextIndex=$((index + 1)) + nextLayout=${layouts[$nextIndex]} + set_kbdlayout "$nextLayout" } diff --git a/scripts/power.sh b/scripts/power.sh index f46fd08..53ef6eb 100755 --- a/scripts/power.sh +++ b/scripts/power.sh @@ -65,18 +65,6 @@ case "$powerAction" in esac try_logout() { - if [[ -n "${SWAYSOCK:-}" ]] && command -v swaymsg &> /dev/null; then - swaymsg -t command exit &> /dev/null - return $? - fi - if [[ -n "${I3SOCK:-}" ]] && command -v i3-msg &> /dev/null; then - i3-msg -t command exit &> /dev/null - return $? - fi - if command -v swaymsg &> /dev/null; then - swaymsg -t command exit &> /dev/null - return $? - fi if command -v i3-msg &> /dev/null; then i3-msg -t command exit &> /dev/null return $? diff --git a/scripts/reminder.sh b/scripts/reminder.sh index 77b09e9..7c5f0d5 100755 --- a/scripts/reminder.sh +++ b/scripts/reminder.sh @@ -42,7 +42,7 @@ add_reminder() { done info="${info#|#}" # Get information for reminder into an array - IFS='|' read -a reminder <<< $info + IFS='|' read -r -a reminder <<< "$info" # Fix time to be 2 digits. [[ ${#reminder[3]} -eq 1 ]] && reminder[3]="0${reminder[3]}" [[ ${#reminder[4]} -eq 1 ]] && reminder[4]="0${reminder[4]}" @@ -83,7 +83,7 @@ add_custom_reminder() { done info="${info#|#}" # Get information for reminder into an array - IFS='|' read -a reminder <<< $info + IFS='|' read -r -a reminder <<< "$info" if [[ "${reminder[0]}" != "# " ]]; then echo "# ${reminder[0]}" >> ~/.reminders fi @@ -112,7 +112,7 @@ add_daily_reminder() { fi # Get information for reminder into an array - IFS='|' read -a reminder <<< $info + IFS='|' read -r -a reminder <<< "$info" # Fix time to be 2 digits. [[ ${#reminder[1]} -eq 1 ]] && reminder[1]="0${reminder[1]}" @@ -153,7 +153,7 @@ add_monthly_reminder() { fi # Get information for reminder into an array - IFS='|' read -a reminder <<< $info + IFS='|' read -r -a reminder <<< "$info" # Fix time and date to be 2 digits. [[ ${#reminder[2]} -eq 1 ]] && reminder[2]="0${reminder[2]}" @@ -205,7 +205,7 @@ add_weekly_reminder() { fi # Get information for reminder into an array - IFS='|' read -a reminder <<< $info + IFS='|' read -r -a reminder <<< "$info" # Fix time to be 2 digits. [[ ${#reminder[9]} -eq 1 ]] && reminder[9]="0${reminder[9]}" @@ -272,27 +272,28 @@ view_reminders() { done # Display the reminders - reminder="$(yad --list --title "I38 - Reminders" --text "Current reminders:" \ + selectedReminder="$(yad --list --title "I38 - Reminders" --text "Current reminders:" \ --column "Reminder" "${yadMenu[@]}" \ --button="Close!gtk-ok:1" --button="Delete!gtk-delete:0" --response=1)" - if [[ $? -ne 0 ]]; then + yadResult=$? + if [[ $yadResult -ne 0 ]]; then return fi - if [[ "${reminder:0:1}" == "#" ]]; then + if [[ "${selectedReminder:0:1}" == "#" ]]; then error "Please select the actual reminder to be deleted, anything starting with # is only a comment. Nothing changed." return fi # Remove the | from the end of reminder - reminder="${reminder%|}" + selectedReminder="${selectedReminder%|}" # Find the index to remove from lines. for i in "${!yadMenu[@]}" ; do - if [[ "${yadMenu[i]}" == "${reminder}" ]]; then + if [[ "${yadMenu[i]}" == "${selectedReminder}" ]]; then # Delete selected reminder and possible preceeding comment. commentIndex=$((i - 1)) if [[ "${lines[commentIndex]:0:1}" == "#" ]]; then - unset lines[$commentIndex] + unset "lines[$commentIndex]" fi - unset lines[$i] + unset "lines[$i]" message "Reminder deleted." printf "%s\n" "${lines[@]}" > ~/.reminders fi @@ -330,11 +331,7 @@ if [[ $# -ne 0 ]]; then url="${url%[[:space:].?!]}" fi if [[ "${#url}" -gt 3 ]]; then - # Copy URL to clipboard using appropriate tool - if [[ -n "${WAYLAND_DISPLAY}" ]] && command -v wl-copy &> /dev/null; then - echo "${url}" | wl-copy - notify-send --hint=int:transient:1 -t 500 -r 38 "${notification} The URL has been copied to the clipboard." - elif command -v xclip &> /dev/null; then + if command -v xclip &> /dev/null; then echo "${url}" | xclip -selection clipboard notify-send --hint=int:transient:1 -t 500 -r 38 "${notification} The URL has been copied to the clipboard." else @@ -347,7 +344,7 @@ if [[ $# -ne 0 ]]; then fi while : ; do - action=$(yad --title "I38 - Reminders" --form \ + yad --title "I38 - Reminders" --form \ --button="_View Today's Reminders!gtk-info":3 \ --button="_View All Reminders!gtk-info":4 \ --button="_Add Reminder!gtk-edit":0 \ @@ -356,9 +353,10 @@ while : ; do --button="Add Monthly Reminder!gtk-edit":6 \ --button="Add Custom Reminder!gtk-edit":7 \ --button="Close!gtk-cancel":1 \ - --separator="") + --separator="" + yadResult=$? - case $? in + case $yadResult in 0) # Single reminder add_reminder diff --git a/scripts/screen_controller.sh b/scripts/screen_controller.sh index 1a7eca1..5e276d8 100755 --- a/scripts/screen_controller.sh +++ b/scripts/screen_controller.sh @@ -10,24 +10,12 @@ # You should have received a copy of the GNU General Public License along with I38. If not, see . - # Not for multiple screens. -# Detect if we're on Wayland or X11 -if [[ -n "${WAYLAND_DISPLAY}" ]]; then - # Sway/Wayland: Get the name of the first output - screenName="$(swaymsg -t get_outputs | jq -r '.[0].name')" - if [[ -z "$screenName" ]]; then - notify-send "Error: Could not detect output" - exit 1 - fi -else - # i3/X11: Get the name of the screen - if ! command -v xrandr &> /dev/null; then - notify-send "Error: xrandr not found" - exit 1 - fi - screenName="$(xrandr --query | grep "connected" | cut -d ' ' -f1 | head -n 1)" +if ! command -v xrandr &> /dev/null; then + notify-send "Error: xrandr not found" + exit 1 fi +screenName="$(xrandr --query | grep "connected" | cut -d ' ' -f1 | head -n 1)" menuOptions=( "1.0" "Maximum Brightness" @@ -43,28 +31,7 @@ if [[ ${#brightness} -lt 1 ]]; then exit 0 fi -# Apply brightness setting -if [[ -n "${WAYLAND_DISPLAY}" ]]; then - # Sway: Use swaymsg to set output brightness - # Note: Sway doesn't have native brightness control, using wlr-randr if available - if command -v wlr-randr &> /dev/null; then - wlr-randr --output "${screenName}" --brightness "${brightness%%|*}" && - spd-say -P important -Cw "Screen set to ${brightness#*|}." - else - # Fallback to gamma adjustment via wl-gammactl or brightnessctl - if command -v brightnessctl &> /dev/null; then - brightnessValue=$(echo "${brightness%%|*} * 100" | bc) - brightnessctl set "${brightnessValue%.*}%" && - spd-say -P important -Cw "Screen set to ${brightness#*|}." - else - notify-send "Error: wlr-randr or brightnessctl required for Sway brightness control" - exit 1 - fi - fi -else - # i3: Use xrandr - xrandr --output "${screenName}" --brightness "${brightness%%|*}" && - spd-say -P important -Cw "Screen set to ${brightness#*|}." -fi +xrandr --output "${screenName}" --brightness "${brightness%%|*}" && + spd-say -P important -Cw "Screen set to ${brightness#*|}." exit 0 diff --git a/scripts/screenlock.sh b/scripts/screenlock.sh index 79f29ed..fdf70b1 100755 --- a/scripts/screenlock.sh +++ b/scripts/screenlock.sh @@ -39,28 +39,19 @@ if ! command -v sha512sum &> /dev/null; then exit 1 fi -wmMsg="i3-msg" -if [[ -n "${SWAYSOCK:-}" ]] && command -v swaymsg &> /dev/null; then - wmMsg="swaymsg" -elif [[ -n "${I3SOCK:-}" ]] && command -v i3-msg &> /dev/null; then - wmMsg="i3-msg" -elif command -v swaymsg &> /dev/null; then - wmMsg="swaymsg" -elif command -v i3-msg &> /dev/null; then - wmMsg="i3-msg" -else - yad --title "I38" --text "No i3 or sway command interface was found for screen lock." --button "Close:0" +if ! command -v i3-msg &> /dev/null; then + yad --title "I38" --text "No i3 command interface was found for screen lock." --button "Close:0" exit 1 fi -currentWorkspace="$($wmMsg -t get_workspaces | jq -r '.[] | select(.focused==true) | .name')" +currentWorkspace="$(i3-msg -t get_workspaces | jq -r '.[] | select(.focused==true) | .name')" lockWorkspace="i38-lock" -if $wmMsg -t get_workspaces | jq -e --arg name "$lockWorkspace" '.[] | select(.name==$name)' &> /dev/null; then +if i3-msg -t get_workspaces | jq -e --arg name "$lockWorkspace" '.[] | select(.name==$name)' &> /dev/null; then lockWorkspace="i38-lock-$$" fi -$wmMsg -t command "workspace --no-auto-back-and-forth \"$lockWorkspace\"" &> /dev/null -$wmMsg -t command "mode screenlock" &> /dev/null +i3-msg -t command "workspace --no-auto-back-and-forth \"$lockWorkspace\"" &> /dev/null +i3-msg -t command "mode screenlock" &> /dev/null attemptCount=0 while : ; do @@ -92,9 +83,9 @@ while : ; do attemptCount=$((attemptCount + 1)) done -$wmMsg -t command "mode default" &> /dev/null +i3-msg -t command "mode default" &> /dev/null if [[ -n "$currentWorkspace" ]]; then - $wmMsg -t command "workspace \"$currentWorkspace\"" &> /dev/null + i3-msg -t command "workspace \"$currentWorkspace\"" &> /dev/null fi exit 0 diff --git a/scripts/window_list.sh b/scripts/window_list.sh index 48b0b53..1d3a764 100755 --- a/scripts/window_list.sh +++ b/scripts/window_list.sh @@ -11,10 +11,6 @@ # You should have received a copy of the GNU General Public License along with I38. If not, see . -path="$(readlink -f $0)" -path="${path%/*/*}" -path="${path##*/}" -if [[ "$path" == "i3" ]]; then mapfile -t windowList < <(python3 -c ' import i3ipc @@ -28,28 +24,4 @@ id="$(yad --title "I38" --list --separator "" --column "id" --column "Select Win if [[ -z "${id}" ]]; then exit 0 fi - i3-msg \[id="${id}"\] focus -else -mapfile -t windowList < <(python3 -c ' -import i3ipc - -i3 = i3ipc.Connection() - -for con in i3.get_tree(): - if con.window or con.type == "con": - if con.name: - print(con.window) - print(con.name)') - -# Remove the first entry if it is "none" -if [[ "${windowList[0]}" == "none" ]]; then - unset "windowList[0]" -fi - -id="$(yad --title "I38" --list --separator "" --column "id" --column "Select Window" --hide-column 1 --print-column 1 "${windowList[@]}")" - -if [[ -z "${id}" ]]; then - exit 0 -fi -swaymsg \[id="${id}"\] focus -fi +i3-msg \[id="${id}"\] focus