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