Sway support removed. Moved I38 specific scripts out of the branch covered by dex.

This commit is contained in:
Storm Dragon
2026-05-14 21:18:02 -04:00
parent 45eb3ae4fc
commit 3a9c2ab6f1
13 changed files with 147 additions and 449 deletions
+37
View File
@@ -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.
+18 -107
View File
@@ -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
+21 -81
View File
@@ -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 <https://www.gnu.org/licenses/>.
# 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
+5 -13
View File
@@ -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
+6 -13
View File
@@ -11,18 +11,11 @@
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.
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} &
+10 -38
View File
@@ -10,53 +10,25 @@
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.
# 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"
+4 -36
View File
@@ -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"
+13 -44
View File
@@ -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"
}
-12
View File
@@ -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 $?
+18 -20
View File
@@ -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
+6 -39
View File
@@ -10,24 +10,12 @@
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.
# 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
+8 -17
View File
@@ -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
+1 -29
View File
@@ -11,10 +11,6 @@
# You should have received a copy of the GNU General Public License along with I38. If not, see <https://www.gnu.org/licenses/>.
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