From dc8f8328405dff922aff80bee043d40de1c5e282 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 6 Apr 2025 19:40:55 -0400 Subject: [PATCH] Reworked the panel. Now it's a mode, like ratpoison mode. New items include panel mode w for weather, and panel mode s for system information. More coming if this turns out to work well. New dependencies added to readme. --- I38.html | 566 ------------------ I38.md | 265 ++++++++ README.md | 1 + i38.sh | 60 +- .../{game_controler.sh => game_controller.sh} | 0 scripts/sysinfo.sh | 110 ++++ scripts/weather.sh | 439 ++++++++++++++ 7 files changed, 862 insertions(+), 579 deletions(-) delete mode 100644 I38.html create mode 100644 I38.md rename scripts/{game_controler.sh => game_controller.sh} (100%) create mode 100755 scripts/sysinfo.sh create mode 100755 scripts/weather.sh diff --git a/I38.html b/I38.html deleted file mode 100644 index 5dda42f..0000000 --- a/I38.html +++ /dev/null @@ -1,566 +0,0 @@ - - - - - - - Welcome to I38 - - - -
-

Welcome to I38

-
-

Welcome to I38 - -Accessible i3 Window Manager

-
-

Note: This help guide has been tailored to your -specific configuration. You’ve chosen BROWSER as your -web browser, MODKEY as your mod key, and you’re using -the SCREENREADER screen reader.

-
-

Introduction to I38

-

I38 is a configuration for the i3 window manager that makes it more -accessible for blind people. It features audio feedback, screen reader -integration, and keyboard shortcuts designed for non-visual -navigation.

-

Unlike traditional desktop environments like GNOME or MATE, i3 is a -tiling window manager, which means windows are arranged in a -non-overlapping layout. This can be more efficient to navigate by -keyboard, as windows are organized in a predictable structure.

-

Coming from GNOME or MATE?

-

If you’re transitioning from GNOME or MATE, here are some key -differences to understand:

- -

I38 has been configured to make this transition easier by providing a -tabbed layout (similar to browser tabs) and shortcuts that may feel -somewhat familiar.

-

Basic Concepts

-

Workspaces

-

Workspaces act like virtual desktops, allowing you to organize -applications. You have 10 workspaces available.

- -

GNOME/MATE comparison: Similar to workspaces in GNOME/MATE, -but with dedicated keyboard shortcuts rather than overview modes or -workspace switchers.

-

Window Management

-

Windows in I38 are arranged in a tabbed layout by default, which -means windows take up the entire screen and you can switch between them -like browser tabs.

- -

GNOME/MATE comparison: Alt+Tab works similarly to -GNOME/MATE, but window placement is automatic rather than manual.

-

Modes in I38

-

Default Mode

-

This is the standard mode for working with applications. Most -commands start with your mod key (MODKEY).

-

Ratpoison Mode

-

Ratpoison mode allows quick access to common actions using shorter -key combinations. To enter Ratpoison mode, press -RATPOISONKEY. After pressing this key, you can execute -commands with single keystrokes.

-

Common Ratpoison mode commands:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyAction
cLaunch a terminal
eOpen text editor (TEXTEDITOR)
wLaunch web browser (BROWSER)
kKill (close) the current window
?Show I38 help
Escape or Control + gExit Ratpoison mode without taking action
Shift + cRestart Cthulhu screen reader
Shift + oRestart Orca screen reader
Shift + tToggle screen reader
Control + ;Reload I38 configuration
Control + qExit i3 (log out)
!Open run dialog
Alt + bCheck battery status
gCheck game controller status
-

GNOME/MATE comparison: This mode has no direct equivalent in -GNOME/MATE. Think of it as a command palette or quick launcher activated -by a single key.

-

Bypass Mode

-

Bypass mode passes all keys directly to the application, which is -useful for applications that need many keyboard shortcuts. To enter -bypass mode, press MODKEY + Shift + -BackSpace. Use the same key combination to exit bypass -mode.

-

GNOME/MATE comparison: In GNOME/MATE, applications always -receive keyboard input directly. Bypass mode simulates this behavior -within i3.

-

Accessibility Features

-

Screen Reader

-

I38 is configured to work with your screen reader (SCREENREADER). The -screen reader will provide spoken feedback about what’s happening on -screen so long as there is a window. If you don’t have a window open and -need to change something SCREENREADER related, press Control+Alt+d to -bring up the desktop, then screen reader keys should work.

- -

GNOME/MATE comparison: GNOME uses Orca by default with its -own keyboard shortcuts. I38 integrates screen readers more deeply with -the window manager.

-

Braille Display Support

-

If you’ve enabled braille display support during setup, I38 will -start XBrlAPI automatically to provide braille output from your screen -reader.

-

OCR (Optical Character -Recognition)

-

If installed, you can use OCR to read text from images or -inaccessible applications:

- -

GNOME/MATE comparison: OCR features are typically not -integrated into GNOME/MATE by default.

-

Sound Effects

-

I38 provides audio feedback for many actions:

- -

This audio feedback provides non-visual confirmation of actions and -state changes.

-

GNOME/MATE comparison: GNOME/MATE typically have fewer sound -effects for window management actions.

-

Application Menu and -Running Programs

-

Access applications in multiple ways:

- -

The applications menu is organized by categories similar to -traditional desktop environments.

-

GNOME/MATE comparison: Instead of clicking on application -icons or using a start menu, I38 provides keyboard shortcuts to access -applications.

-

Reminders and Notifications

-

I38 includes integration with the remind program for -managing reminders:

- -

The reminder tool provides the following features:

- -

GNOME/MATE comparison: Similar to calendar applications in -GNOME/MATE but with a simplified interface optimized for keyboard -navigation.

-

Volume and Media Controls

-

System Volume

- -

Media Player Controls

- -

In Ratpoison mode, these are also available with Alt+Shift -combinations:

- -

GNOME/MATE comparison: Media controls are similar to those -in GNOME/MATE, with the addition of audio feedback.

-

File Management

-

I38 uses FILEBROWSER for file management. Launch it in Ratpoison mode -with the f key.

-

GNOME/MATE comparison: Similar functionality to Nautilus -(GNOME) or Caja (MATE), but launched via keyboard shortcut rather than -from a desktop icon or menu.

-

System Operations

- -

GNOME/MATE comparison: These functions are typically -available through system menus or indicators in GNOME/MATE.

-

Keyboard Layouts

-

Switch between layouts: Super + Space

-

This is only available if you chose multiple keyboard layouts during -setup.

-

GNOME/MATE comparison: Similar to keyboard layout switching -in GNOME/MATE, but with a different default shortcut.

-

Desktop and Window -Decorations

-

Unlike GNOME or MATE, i3 uses minimal window decorations. Windows -don’t have title bars with minimize/maximize buttons. Instead, windows -fill their available space automatically, and interactions are performed -through keyboard shortcuts.

- -

Clipboard Management

-

I38 includes clipboard management features:

- -

Bookmark Management

- -

GNOME/MATE comparison: Bookmarks are typically managed -within applications in GNOME/MATE. I38 provides a system-wide bookmark -manager.

-

Tips for New Users

- -

Transitioning from GNOME/MATE

- -

Customization

-

You can customize I38 by editing the file -~/.config/i3/customizations. This file will not be -overwritten when you update I38.

-

Example customizations:

-
# Change background color
-exec_always --no-startup-id xsetroot -solid "#2E3440"
-
-# Add custom keybinding
-bindsym $mod+F12 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ 100%
-

To reconfigure I38 completely, run the i38.sh script -again.

-

GNOME/MATE comparison: Much more text-based configuration -compared to the graphical settings dialogs in GNOME/MATE.

-

Getting Help

-

If you need assistance with I38, you can:

- -
-

I38 - Making i3 accessible. A Stormux project. License: GPL -v3

- - diff --git a/I38.md b/I38.md new file mode 100644 index 0000000..7106f7f --- /dev/null +++ b/I38.md @@ -0,0 +1,265 @@ +# Welcome to I38 - Accessible i3 Window Manager + +> **Note:** This help guide has been tailored to your specific configuration. You've chosen **BROWSER** as your web browser, **MODKEY** as your mod key, and you're using the **SCREENREADER** screen reader. + +## Introduction to I38 + +I38 is a configuration for the i3 window manager that makes it more accessible for blind people. It features audio feedback, screen reader integration, and keyboard shortcuts designed for non-visual navigation. + +Unlike traditional desktop environments like GNOME or MATE, i3 is a tiling window manager, which means windows are arranged in a non-overlapping layout. This can be more efficient to navigate by keyboard, as windows are organized in a predictable structure. + +### Coming from GNOME or MATE? + +If you're transitioning from GNOME or MATE, here are some key differences to understand: + +- **Window Management**: In GNOME/MATE, windows can overlap freely and are typically manipulated with a mouse. In i3/I38, windows tile automatically and are primarily controlled with keyboard shortcuts. +- **Panels and Indicators**: Instead of persistent panels with menus and indicators, I38 uses keyboard shortcuts to access functionality. +- **Workspace Navigation**: While GNOME/MATE have workspaces that you can switch between, I38's workspaces are more central to the workflow and are accessed via dedicated keyboard shortcuts. +- **Application Launching**: Rather than using a start menu or activities overview, I38 provides keyboard shortcuts for launching applications. + +I38 has been configured to make this transition easier by providing a tabbed layout (similar to browser tabs) and shortcuts that may feel somewhat familiar. + +## Basic Concepts + +### Workspaces + +Workspaces act like virtual desktops, allowing you to organize applications. You have 10 workspaces available. + +- Switch to workspace: `Control` + `F1` through `F10` +- Move window to workspace: `Control` + `Shift` + `F1` through `F10` + +*GNOME/MATE comparison:* Similar to workspaces in GNOME/MATE, but with dedicated keyboard shortcuts rather than overview modes or workspace switchers. + +### Window Management + +Windows in I38 are arranged in a tabbed layout by default, which means windows take up the entire screen and you can switch between them like browser tabs. + +- Switch between windows: `Alt` + `Tab` (next) or `Alt` + `Shift` + `Tab` (previous) +- Launch terminal: `MODKEY` + `Return` +- Close window: `MODKEY` + `F4` +- Toggle fullscreen: `MODKEY` + `BackSpace` +- List windows in current workspace: `RATPOISONKEY` then `'` (apostrophe) + +*GNOME/MATE comparison:* Alt+Tab works similarly to GNOME/MATE, but window placement is automatic rather than manual. + +## Modes in I38 + +### Default Mode + +This is the standard mode for working with applications. Most commands start with your mod key (`MODKEY`). + +### Ratpoison Mode + +Ratpoison mode allows quick access to common actions using shorter key combinations. To enter Ratpoison mode, press `RATPOISONKEY`. After pressing this key, you can execute commands with single keystrokes. + +Common Ratpoison mode commands: + +| Key | Action | +|-----|--------| +| `c` | Launch a terminal | +| `e` | Open text editor (TEXTEDITOR) | +| `w` | Launch web browser (BROWSER) | +| `k` | Kill (close) the current window | +| `?` | Show I38 help | +| `Escape` or `Control` + `g` | Exit Ratpoison mode without taking action | +| `Shift` + `c` | Restart Cthulhu screen reader | +| `Shift` + `o` | Restart Orca screen reader | +| `Shift` + `t` | Toggle screen reader | +| `Control` + `;` | Reload I38 configuration | +| `Control` + `q` | Exit i3 (log out) | +| `!` | Open run dialog | +| `Alt` + `b` | Check battery status | +| `g` | Check game controller status | + +*GNOME/MATE comparison:* This mode has no direct equivalent in GNOME/MATE. Think of it as a command palette or quick launcher activated by a single key. + +### Bypass Mode + +Bypass mode passes all keys directly to the application, which is useful for applications that need many keyboard shortcuts. To enter bypass mode, press `MODKEY` + `Shift` + `BackSpace`. Use the same key combination to exit bypass mode. + +*GNOME/MATE comparison:* In GNOME/MATE, applications always receive keyboard input directly. Bypass mode simulates this behavior within i3. + +## Accessibility Features + +### Screen Reader + +I38 is configured to work with your screen reader (SCREENREADER). The screen reader will provide spoken feedback about what's happening on screen so long as there is a window. If you don't have a window open and need to change something SCREENREADER related, press Control+Alt+d to bring up the desktop, then screen reader keys should work. + +- Toggle screen reader: `RATPOISONKEY` then `Shift` + `t` +- Restart screen reader: `RATPOISONKEY` then `Shift` + `o` (for Orca) or `Shift` + `c` (for Cthulhu) +- Interrupt speech: `MODKEY` + `Shift` + `F5` + +*GNOME/MATE comparison:* GNOME uses Orca by default with its own keyboard shortcuts. I38 integrates screen readers more deeply with the window manager. + +### Braille Display Support + +If you've enabled braille display support during setup, I38 will start XBrlAPI automatically to provide braille output from your screen reader. + +### OCR (Optical Character Recognition) + +If installed, you can use OCR to read text from images or inaccessible applications: + +- `MODKEY` + `F5`: Perform OCR on the entire screen and speak the content +- In Ratpoison mode: `Print` or `MODKEY` + `r`: Perform OCR and save to clipboard + +*GNOME/MATE comparison:* OCR features are typically not integrated into GNOME/MATE by default. + +### Sound Effects + +I38 provides audio feedback for many actions: + +- Window open/close: Ascending/descending tones +- Mode changes: Distinctive sounds for each mode +- Workspace changes: Subtle audio cues +- Fullscreen toggle: Special sound effect + +This audio feedback provides non-visual confirmation of actions and state changes. + +*GNOME/MATE comparison:* GNOME/MATE typically have fewer sound effects for window management actions. + +## Application Menu and Running Programs + +Access applications in multiple ways: + +- Applications menu: `MODKEY` + `F1` +- Run dialog (enter a command): `MODKEY` + `F2` or in Ratpoison mode, `!` (exclamation mark) +- Common applications have dedicated shortcuts in Ratpoison mode (see table above) + +The applications menu is organized by categories similar to traditional desktop environments. + +*GNOME/MATE comparison:* Instead of clicking on application icons or using a start menu, I38 provides keyboard shortcuts to access applications. + +## Reminders and Notifications + +I38 includes integration with the `remind` program for managing reminders: + +- Access the reminder tool: `RATPOISONKEY` then `r` +- Create various types of reminders (one-time, daily, weekly, monthly) +- Get notification alerts for your reminders + +The reminder tool provides the following features: + +- **One-time Reminders**: Set for a specific date and time +- **Daily Reminders**: Occur every day at the specified time +- **Weekly Reminders**: Occur on specific days of the week +- **Monthly Reminders**: Occur on a specific day each month or the last day of each month +- **Custom Reminders**: Create complex reminder patterns + +*GNOME/MATE comparison:* Similar to calendar applications in GNOME/MATE but with a simplified interface optimized for keyboard navigation. + +## Volume and Media Controls + +### System Volume + +- Increase volume: `MODKEY` + `XF86AudioRaiseVolume` +- Decrease volume: `MODKEY` + `XF86AudioLowerVolume` +- Mute/unmute: `MODKEY` + `XF86AudioMute` + +### Media Player Controls + +- Play/Pause: `XF86AudioPlay` +- Next track: `XF86AudioNext` +- Previous track: `XF86AudioPrev` +- Stop: `XF86AudioStop` +- Media info: `MODKEY` + `XF86AudioPlay` + +In Ratpoison mode, these are also available with Alt+Shift combinations: + +- Increase volume: `Alt` + `Shift` + `=` +- Decrease volume: `Alt` + `Shift` + `-` +- Previous track: `Alt` + `Shift` + `z` +- Pause: `Alt` + `Shift` + `c` +- Play: `Alt` + `Shift` + `x` +- Stop: `Alt` + `Shift` + `v` +- Next track: `Alt` + `Shift` + `b` +- Media info: `Alt` + `Shift` + `u` + +*GNOME/MATE comparison:* Media controls are similar to those in GNOME/MATE, with the addition of audio feedback. + +## File Management + +I38 uses FILEBROWSER for file management. Launch it in Ratpoison mode with the `f` key. + +*GNOME/MATE comparison:* Similar functionality to Nautilus (GNOME) or Caja (MATE), but launched via keyboard shortcut rather than from a desktop icon or menu. + +## System Operations + +- Reload I38 configuration: In Ratpoison mode, `Control` + `;` (semicolon) +- Exit i3 (log out): In Ratpoison mode, `Control` + `q` +- Check battery status: In Ratpoison mode, `Alt` + `b` +- Check game controller status: In Ratpoison mode, `g` +- Adjust screen brightness (if xrandr is available): In Ratpoison mode, `Alt` + `s` + +*GNOME/MATE comparison:* These functions are typically available through system menus or indicators in GNOME/MATE. + +## Keyboard Layouts + +Switch between layouts: `Super` + `Space` + +This is only available if you chose multiple keyboard layouts during setup. + +*GNOME/MATE comparison:* Similar to keyboard layout switching in GNOME/MATE, but with a different default shortcut. + +## Desktop and Window Decorations + +Unlike GNOME or MATE, i3 uses minimal window decorations. Windows don't have title bars with minimize/maximize buttons. Instead, windows fill their available space automatically, and interactions are performed through keyboard shortcuts. + +- Show desktop icons: `MODKEY` + `Control` + `d` + +## Clipboard Management + +I38 includes clipboard management features: + +- Access clipboard history: `MODKEY` + `Control` + `c` + +## Bookmark Management + +- Access bookmarks: `MODKEY` + `Control` + `b` + +*GNOME/MATE comparison:* Bookmarks are typically managed within applications in GNOME/MATE. I38 provides a system-wide bookmark manager. + +## Tips for New Users + +- **Start with Ratpoison mode**: Learn the single-key commands first, as they're easier to remember. +- **Use the window list**: When you're lost, use `RATPOISONKEY` then `'` to show all windows in the current workspace. +- **Bookmark important websites**: Use `MODKEY` + `Control` + `b` to access bookmarks. +- **Remember the help shortcut**: `MODKEY` + `Shift` + `F1` is your friend when you need guidance. +- **Let the sound effects guide you**: Pay attention to the audio cues to understand what's happening. +- **Take advantage of OCR**: If an application isn't accessible, try the OCR function. + +### Transitioning from GNOME/MATE + +- Start by learning the basic navigation shortcuts before exploring advanced features +- The tabbed layout should feel somewhat familiar if you're used to browser tabs +- Alt+Tab works similarly to GNOME/MATE for switching between windows +- Focus on keyboard commands rather than looking for visual elements like panels or docks + +## Customization + +You can customize I38 by editing the file `~/.config/i3/customizations`. This file will not be overwritten when you update I38. + +Example customizations: + +``` +# Change background color +exec_always --no-startup-id xsetroot -solid "#2E3440" + +# Add custom keybinding +bindsym $mod+F12 exec --no-startup-id pactl set-sink-volume @DEFAULT_SINK@ 100% +``` + +To reconfigure I38 completely, run the `i38.sh` script again. + +*GNOME/MATE comparison:* Much more text-based configuration compared to the graphical settings dialogs in GNOME/MATE. + +## Getting Help + +If you need assistance with I38, you can: + +- Press `MODKEY` + `Shift` + `F1` to view the help documentation +- Visit the Stormux website at [stormux.org](https://stormux.org) +- Check the i3 documentation at [i3wm.org/docs/userguide.html](https://i3wm.org/docs/userguide.html) + +--- + +*I38 - Making i3 accessible. A Stormux project. License: GPL v3* diff --git a/README.md b/README.md index ba29769..93472aa 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ An uppercase I looks like a 1, 3 from i3, and 8 because the song [We Are 138](ht - notification-daemon: To handle notifications - xfce4-notifyd: For sending notifications Replaces notification-daemon (Sway user will need to install the customized variant at ) - ocrdesktop: For getting contents of the current window with OCR. +- pandoc or markdown: To generate html files. - pamixer: for the mute-unmute script - pcmanfm: [optional] Graphical file manager. - playerctl: music controls diff --git a/i38.sh b/i38.sh index eb57208..d50b108 100755 --- a/i38.sh +++ b/i38.sh @@ -33,6 +33,11 @@ if [[ -n "${missing}" ]]; then echo "${missing[*]}" exit 1 fi +if ! command -v pandoc &> /dev/null && ! command -v markdown &> /dev/null ; then + echo "Please install either pandoc or markdown." + echo "The markdown command may be provided by the package discount." + exit 1 +fi keyboard_menu() { keyboardMenu=("us" "English (US)" @@ -539,9 +544,6 @@ bindsym Control+F8 workspace number \$ws8, exec --no-startup-id ${i3Path}/script bindsym Control+F9 workspace number \$ws9, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh bindsym Control+F10 workspace number \$ws10, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh -# This is sort of a fake panel where some useful but seldom interacted with applications can live. -bindsym Control+Mod1+Tab workspace number 11, exec --no-startup-id ${i3Path}/scripts/announce_workspace.sh - # move focused container to workspace bindsym Control+Shift+F1 move container to workspace number \$ws1, exec spd-say -P important -Cw "moved to workspace 1" bindsym Control+Shift+F2 move container to workspace number \$ws2, exec spd-say -P important -Cw "moved to workspace 2" @@ -578,6 +580,23 @@ if [[ ${#kbd[@]} -gt 1 ]]; then echo "bindsym Mod4+space exec ${i3Path}/scripts/keyboard.sh cycle ${kbd[@]}" >> ${i3Path}/config fi +# Create panel mode +cat << EOF >> ${i3Path}/config +# Panel mode configuration +bindsym Control+Mod1+Tab mode "panel" +mode "panel" { + # Weather information bound to w + bindsym w exec --no-startup-id ${i3Path}/scripts/weather.sh, mode "default" + + # System information bound to s + bindsym s exec --no-startup-id ${i3Path}/scripts/sysinfo.sh, mode "default" + + # Exit panel mode without any action + bindsym Escape mode "default" + bindsym Control+g mode "default" +} +EOF + # Create ratpoison mode if requested. if [[ -n "${escapeKey}" ]]; then cat << EOF >> ${i3Path}/config @@ -641,7 +660,7 @@ bindsym Mod1+Shift+u exec --no-startup-id play -qV0 "| sox -np synth 0.03 sin 20 #Check battery status bindsym Mod1+b exec --no-startup-id ${i3Path}/scripts/battery_status.sh, mode "default" #Check controller battery status -bindsym g exec ${i3Path}/scripts/game_controler.sh -s, mode "default" +bindsym g exec ${i3Path}/scripts/game_controller.sh -s, mode "default" # Get a list of windows in the current workspace bindsym apostrophe exec --no-startup-id ${i3Path}/scripts/window_list.sh, mode "default" # Restart Cthulhu @@ -676,7 +695,7 @@ fi cat << EOF >> ${i3Path}/config # Auto start section -$(if [[ $sounzzds -eq 0 ]]; then +$(if [[ $sounds -eq 0 ]]; then if [[ $usingSway -eq 0 ]]; then echo "exec --no-startup-id ${i3Path}/scripts/sound.py" else @@ -725,19 +744,34 @@ fi) # First run help documentation exec --no-startup-id bash -c 'if [[ -f "${i3Path}/firstrun" ]]; then ${webBrowser} "${i3Path}/I38.html"& rm "${i3Path}/firstrun"; fi' -# Fake panel setup -exec --no-startup-id i3-msg 'workspace 11; workspace 1' - # If you want to add personal customizations to i3, add them in ${i3Path}/customizations # It is not overwritten when the config file is recreated. include "${i3Path}/customizations" -# Applications that will be placed in workspace 11, which we use as a sort of fake panel. - include "${i3Path}/panel.conf" EOF touch "${i3Path}/customizations" -cp -v panel.conf "${i3Path}/panel.conf" -# Move html help file to destination -cp -v I38.html "${i3Path}/I38.html" +# Check for markdown or pandoc for converting the welcome document +if command -v pandoc &> /dev/null ; then + pandoc -f markdown -t html "I38.md" -so "${i3Path}/I38.html" --metadata title="Welcome to I38" +elif command -v markdown &> /dev/null ; then + cat << EOF > "${i3Path}/I38.html" + + + + Welcome to I38 + + + +EOF + + # Convert markdown to html and append to the file + markdown "I38.md" >> "${i3Path}/I38.html" + + # Close the HTML tags using heredoc + cat << EOF >> "${i3Path}/I38.html" + + +EOF +fi # More readable version of variables. escapeKey="${escapeKey//Mod1/Alt}" diff --git a/scripts/game_controler.sh b/scripts/game_controller.sh similarity index 100% rename from scripts/game_controler.sh rename to scripts/game_controller.sh diff --git a/scripts/sysinfo.sh b/scripts/sysinfo.sh new file mode 100755 index 0000000..3cd8a3d --- /dev/null +++ b/scripts/sysinfo.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + + +# Initialize variables +cpuUsage=0 +cpuTemp=0 +memoryUsed=0 +memoryTotal=0 +memoryPercent=0 +swapUsed=0 +swapTotal=0 +swapPercent=0 +diskUsed=0 +diskTotal=0 +diskPercent=0 +networkSent=0 +networkRecv=0 + +# Helper function for temperature conversion +celsius_to_fahrenheit() { + local celsius="$1" + [[ -z "$celsius" || "$celsius" == "--" ]] && echo "--" && return + [[ ! "$celsius" =~ ^-?[0-9]+(\.[0-9]+)?$ ]] && echo "--" && return + + local fahrenheit + fahrenheit=$(echo "scale=1; ($celsius * 9/5) + 32" | bc -l) + echo "$fahrenheit" +} + +update_system_data() { + # CPU usage + cpuUsage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}') + cpuUsage=$(printf "%.1f" "$cpuUsage" 2>/dev/null || echo "$cpuUsage") + + # CPU temperature - fix for high readings + local tempCelsius="--" + if [[ -f /sys/class/thermal/thermal_zone0/temp ]]; then + tempCelsius=$(cat /sys/class/thermal/thermal_zone0/temp) + # Check if we need to divide by 1000 (common format) + if [[ $tempCelsius -gt 200 ]]; then + tempCelsius=$(echo "scale=1; $tempCelsius/1000" | bc -l) + fi + elif [[ -f /sys/class/hwmon/hwmon0/temp1_input ]]; then + tempCelsius=$(cat /sys/class/hwmon/hwmon0/temp1_input) + if [[ $tempCelsius -gt 200 ]]; then + tempCelsius=$(echo "scale=1; $tempCelsius/1000" | bc -l) + fi + elif command -v sensors &>/dev/null; then + tempCelsius=$(sensors | grep -oP 'Core 0.*?\+\K[0-9.]+') + fi + + [[ "$tempCelsius" != "--" && "$tempCelsius" != "null" ]] && cpuTemp=$(celsius_to_fahrenheit "$tempCelsius") || cpuTemp="--" + + # Memory usage + memoryTotal=$(free -m | awk '/^Mem:/{print $2/1024}') + memoryUsed=$(free -m | awk '/^Mem:/{print $3/1024}') + memoryPercent=$(free | awk '/^Mem:/{printf("%.1f", $3/$2 * 100)}') + + # Swap usage + swapTotal=$(free -m | awk '/^Swap:/{print $2/1024}') + swapUsed=$(free -m | awk '/^Swap:/{print $3/1024}') + [[ "$swapTotal" -gt 0 ]] && swapPercent=$(free | awk '/^Swap:/{printf("%.1f", $3/$2 * 100)}') || swapPercent=0 + + # Disk usage + diskTotal=$(df -h / | awk 'NR==2 {print $2}') + diskUsed=$(df -h / | awk 'NR==2 {print $3}') + diskPercent=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%') + + # Network usage + networkSent=$(cat /proc/net/dev | grep -v "lo:" | awk '{s+=$10} END {printf "%.2f", s/1024/1024}') + networkRecv=$(cat /proc/net/dev | grep -v "lo:" | awk '{r+=$2} END {printf "%.2f", r/1024/1024}') +} + +display_system_info() { + update_system_data + + # Create the system information text with proper line breaks + systemInfoText="System Information + +CPU +Usage: ${cpuUsage}% +Temperature: ${cpuTemp}° F + +Memory +Usage: ${memoryUsed} / ${memoryTotal} GB (${memoryPercent}%) +Swap: ${swapUsed} / ${swapTotal} GB (${swapPercent}%) + +Disk (Root Partition) +Usage: ${diskUsed} / ${diskTotal} GB (${diskPercent}%) + +Network (Total Since Boot) +Received: ${networkRecv} MB +Sent: ${networkSent} MB + +End of text. Press Control+Home to return to the beginning." + + # Display in text-info dialog for screen reader accessibility + echo "$systemInfoText" | yad --pname=I38System \ + --title="I38 System Information" \ + --text-info \ + --show-cursor \ + --width=400 \ + --height=400 \ + --center \ + --button="Close:0" +} + +display_system_info + +exit 0 diff --git a/scripts/weather.sh b/scripts/weather.sh new file mode 100755 index 0000000..6ad0821 --- /dev/null +++ b/scripts/weather.sh @@ -0,0 +1,439 @@ +#!/usr/bin/env bash + + +# Configuration settings +defaultCity="Raleigh, NC" +defaultLat="35.78" +defaultLon="-78.64" +tempUnit="F" +updateInterval=30 + +configDir="${XDG_CONFIG_HOME:-$HOME/.config}/stormux/I38" +configFile="$configDir/weather.conf" +mkdir -p "$configDir" + +# Initialize variables +cityName="Detecting..." +latitude=0.0 +longitude=0.0 +currentTemp="--" +currentHumidity="--" +currentWindSpeed="--" +currentWindSpeedMph="--" +currentConditions="Unknown" +weatherLastUpdate=0 +severeWeatherAlerted=0 + +declare -a forecastDates=("--" "--" "--") +declare -a forecastFormattedDates=("--" "--" "--") +declare -a forecastMinTemps=("--" "--" "--") +declare -a forecastMaxTemps=("--" "--" "--") +declare -a forecastConditions=("Unknown" "Unknown" "Unknown") + +declare -A weatherCodes +weatherCodes[0]="Clear sky" +weatherCodes[1]="Mainly clear" +weatherCodes[2]="Partly cloudy" +weatherCodes[3]="Overcast" +weatherCodes[45]="Fog" +weatherCodes[48]="Rime fog" +weatherCodes[51]="Light drizzle" +weatherCodes[53]="Moderate drizzle" +weatherCodes[55]="Dense drizzle" +weatherCodes[56]="Light freezing drizzle" +weatherCodes[57]="Dense freezing drizzle" +weatherCodes[61]="Slight rain" +weatherCodes[63]="Moderate rain" +weatherCodes[65]="Heavy rain" +weatherCodes[66]="Light freezing rain" +weatherCodes[67]="Heavy freezing rain" +weatherCodes[71]="Slight snow fall" +weatherCodes[73]="Moderate snow fall" +weatherCodes[75]="Heavy snow fall" +weatherCodes[77]="Snow flurries" +weatherCodes[80]="Slight rain showers" +weatherCodes[81]="Moderate rain showers" +weatherCodes[82]="Heavy rain showers" +weatherCodes[85]="Slight snow showers" +weatherCodes[86]="Heavy snow showers" +weatherCodes[95]="Thunderstorm" +weatherCodes[96]="Thunderstorm with slight hail" +weatherCodes[99]="Thunderstorm with heavy hail" + +declare -a severeWeatherCodes=(65 67 75 82 86 95 96 99) + +# Button return codes +refreshBtn=0 +quitBtn=1 +settingsBtn=2 + +trap "pkill -P $$" EXIT INT TERM + +# Load configuration if available +if [ -f "$configFile" ]; then + source "$configFile" + # Convert lastWeatherUpdate string to integer if it exists + [[ -n "$lastWeatherUpdate" ]] && weatherLastUpdate=$lastWeatherUpdate || weatherLastUpdate=0 + + if [[ -n "$city" ]]; then + cityName="$city" + latitude="$latitude" + longitude="$longitude" + fi + + # Try to reload saved weather data + if [[ "$weatherLastUpdate" -gt 0 && "$currentTemp" == "--" ]]; then + [[ -n "$savedCurrentTemp" ]] && currentTemp="$savedCurrentTemp" + [[ -n "$savedCurrentHumidity" ]] && currentHumidity="$savedCurrentHumidity" + [[ -n "$savedCurrentConditions" ]] && currentConditions="$savedCurrentConditions" + [[ -n "$savedCurrentWindSpeed" ]] && currentWindSpeedMph="$savedCurrentWindSpeed" + + for i in {0..2}; do + varDate="savedForecastDate_$i" + varMin="savedForecastMin_$i" + varMax="savedForecastMax_$i" + varCond="savedForecastCond_$i" + + [[ -n "${!varDate}" ]] && forecastFormattedDates[$i]="${!varDate}" + [[ -n "${!varMin}" ]] && forecastMinTemps[$i]="${!varMin}" + [[ -n "${!varMax}" ]] && forecastMaxTemps[$i]="${!varMax}" + [[ -n "${!varCond}" ]] && forecastConditions[$i]="${!varCond}" + done + fi +fi + +# Helper functions +time_diff() { + local timestamp="$1" + local now=$(date +%s) + local diff=$((now - timestamp)) + + if [ $diff -lt 60 ]; then + echo "just now" + elif [ $diff -lt 3600 ]; then + local minutes=$((diff / 60)) + echo "$minutes minute$([ $minutes -ne 1 ] && echo "s") ago" + elif [ $diff -lt 86400 ]; then + local hours=$((diff / 3600)) + echo "$hours hour$([ $hours -ne 1 ] && echo "s") ago" + else + local days=$((diff / 86400)) + echo "$days day$([ $days -ne 1 ] && echo "s") ago" + fi +} + +celsius_to_fahrenheit() { + local celsius="$1" + [[ -z "$celsius" || "$celsius" == "--" ]] && echo "--" && return + [[ ! "$celsius" =~ ^-?[0-9]+(\.[0-9]+)?$ ]] && echo "--" && return + + local fahrenheit + fahrenheit=$(echo "scale=1; ($celsius * 9/5) + 32" | bc -l) + echo "$fahrenheit" +} + +kmh_to_mph() { + local kmh="$1" + [[ -z "$kmh" || "$kmh" == "--" ]] && echo "--" && return + [[ ! "$kmh" =~ ^-?[0-9]+(\.[0-9]+)?$ ]] && echo "--" && return + + local mph + mph=$(echo "scale=1; $kmh * 0.621371" | bc -l) + echo "$mph" +} + +format_date() { + local isoDate="$1" + [[ -z "$isoDate" || "$isoDate" == "--" ]] && echo "--" && return + date -d "$isoDate" "+%A, %B %d" 2>/dev/null || echo "$isoDate" +} + +# Save configuration +save_config() { + cat > "$configFile" << EOF +city="$cityName" +latitude="$latitude" +longitude="$longitude" +tempUnit=$tempUnit +updateInterval=$updateInterval +lastWeatherUpdate=$weatherLastUpdate +savedCurrentTemp="$currentTemp" +savedCurrentHumidity="$currentHumidity" +savedCurrentConditions="$currentConditions" +savedCurrentWindSpeed="$currentWindSpeedMph" +savedForecastDate_0="${forecastFormattedDates[0]}" +savedForecastMin_0="${forecastMinTemps[0]}" +savedForecastMax_0="${forecastMaxTemps[0]}" +savedForecastCond_0="${forecastConditions[0]}" +savedForecastDate_1="${forecastFormattedDates[1]}" +savedForecastMin_1="${forecastMinTemps[1]}" +savedForecastMax_1="${forecastMaxTemps[1]}" +savedForecastCond_1="${forecastConditions[1]}" +savedForecastDate_2="${forecastFormattedDates[2]}" +savedForecastMin_2="${forecastMinTemps[2]}" +savedForecastMax_2="${forecastMaxTemps[2]}" +savedForecastCond_2="${forecastConditions[2]}" +EOF +} + +# Play severe weather alert sound using Sox +play_severe_weather_alert() { + if command -v play &>/dev/null; then + # Generate alert sound pattern using sox + play -nqV0 synth 2 sine 853 sine 960 remix - norm -15 & + fi + + # Also display notification if available + if command -v notify-send &>/dev/null; then + notify-send "Severe Weather Alert" "Severe weather conditions detected for $cityName: $currentConditions" -u critical + fi +} + +# Function to check if a value is in array +in_array() { + local value="$1" + shift + local array=("$@") + + for item in "${array[@]}"; do + if [[ "$item" == "$value" ]]; then + return 0 # True, found in array + fi + done + return 1 # False, not found +} + +# Function to detect location +get_location() { + # Only try location detection if we don't already have a city name + if [[ "$cityName" == "Detecting..." ]]; then + echo "Attempting to detect location via ipinfo.io..." + + # Try to fetch location data + local locationData + locationData=$(curl -s --connect-timeout 5 "https://ipinfo.io/json" 2>/dev/null) + + if [[ $? -eq 0 && -n "$locationData" && $(echo "$locationData" | jq -e '.city') ]]; then + echo "Location data received successfully" + cityName=$(echo "$locationData" | jq -r '.city // "Unknown"') + local region=$(echo "$locationData" | jq -r '.region // ""') + # Add region/state to city name if available + [[ -n "$region" ]] && cityName="$cityName, $region" + + # Extract coordinates directly from the "loc" field + local loc=$(echo "$locationData" | jq -r '.loc // "0,0"') + latitude=$(echo "$loc" | cut -d',' -f1) + longitude=$(echo "$loc" | cut -d',' -f2) + save_config + else + cityName="$defaultCity" + latitude="$defaultLat" + longitude="$defaultLon" + save_config + fi + fi +} + +# Function to fetch weather data +fetch_weather_data() { + local now=$(date +%s) + local elapsedMinutes=$(( (now - weatherLastUpdate) / 60 )) + + # Only fetch if needed + if [[ $weatherLastUpdate -eq 0 || $elapsedMinutes -ge $updateInterval ]]; then + local url="https://api.open-meteo.com/v1/forecast?latitude=$latitude&longitude=$longitude¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m&daily=weather_code,temperature_2m_max,temperature_2m_min&timezone=auto" + local response=$(curl -s --connect-timeout 10 "$url" 2>/dev/null) + + if [[ $? -eq 0 && -n "$response" && $(echo "$response" | jq -e '.current' 2>/dev/null) ]]; then + # Update current weather data + local tempCelsius=$(echo "$response" | jq -r '.current.temperature_2m // "--"' 2>/dev/null) + [[ "$tempCelsius" != "--" && "$tempCelsius" != "null" ]] && currentTemp=$(celsius_to_fahrenheit "$tempCelsius") || currentTemp="--" + + currentHumidity=$(echo "$response" | jq -r '.current.relative_humidity_2m // "--"' 2>/dev/null) + [[ "$currentHumidity" == "null" ]] && currentHumidity="--" + + currentWindSpeed=$(echo "$response" | jq -r '.current.wind_speed_10m // "--"' 2>/dev/null) + if [[ "$currentWindSpeed" != "--" && "$currentWindSpeed" != "null" ]]; then + currentWindSpeedMph=$(kmh_to_mph "$currentWindSpeed") + else + currentWindSpeed="--" + currentWindSpeedMph="--" + fi + + local weatherCode=$(echo "$response" | jq -r '.current.weather_code // 0' 2>/dev/null) + [[ "$weatherCode" == "null" ]] && weatherCode=0 + currentConditions="${weatherCodes[$weatherCode]:-Unknown}" + + # Check for severe weather and play alert if needed + if in_array "$weatherCode" "${severeWeatherCodes[@]}"; then + if [ "$severeWeatherAlerted" -eq 0 ]; then + play_severe_weather_alert + severeWeatherAlerted=1 + fi + else + # Reset alert flag if weather is no longer severe + severeWeatherAlerted=0 + fi + + # Process forecast data (limited to 3 days) + if [[ $(echo "$response" | jq -e '.daily' 2>/dev/null) ]]; then + for i in {0..2}; do + # Process forecast data + forecastDates[$i]=$(echo "$response" | jq -r ".daily.time[$i] // \"--\"" 2>/dev/null) + [[ "${forecastDates[$i]}" != "--" && "${forecastDates[$i]}" != "null" ]] && \ + forecastFormattedDates[$i]=$(format_date "${forecastDates[$i]}") || forecastFormattedDates[$i]="--" + + local minTempC=$(echo "$response" | jq -r ".daily.temperature_2m_min[$i] // \"--\"" 2>/dev/null) + [[ "$minTempC" != "--" && "$minTempC" != "null" ]] && \ + forecastMinTemps[$i]=$(celsius_to_fahrenheit "$minTempC") || forecastMinTemps[$i]="--" + + local maxTempC=$(echo "$response" | jq -r ".daily.temperature_2m_max[$i] // \"--\"" 2>/dev/null) + [[ "$maxTempC" != "--" && "$maxTempC" != "null" ]] && \ + forecastMaxTemps[$i]=$(celsius_to_fahrenheit "$maxTempC") || forecastMaxTemps[$i]="--" + + local code=$(echo "$response" | jq -r ".daily.weather_code[$i] // 0" 2>/dev/null) + [[ "$code" == "null" ]] && code=0 + forecastConditions[$i]="${weatherCodes[$code]:-Unknown}" + done + fi + + # Update timestamp + weatherLastUpdate=$(date +%s) + save_config + else + echo "Failed to fetch weather data. Response code: $?" + if [[ -n "$response" ]]; then + echo "First 100 chars of response: ${response:0:100}" + fi + fi + fi +} + +# Function to change location (for settings) +change_location() { + local newLocation="$1" + + if [[ -n "$newLocation" && "$newLocation" != "$cityName" ]]; then + # Try to parse the location using curl to a geocoding service + local result=$(curl -s --connect-timeout 10 "https://nominatim.openstreetmap.org/search?q=$newLocation&format=json" 2>/dev/null) + + if [[ -n "$result" && $(echo "$result" | jq -e '.[0]') ]]; then + cityName="$newLocation" + latitude=$(echo "$result" | jq -r '.[0].lat // "0.0"') + longitude=$(echo "$result" | jq -r '.[0].lon // "0.0"') + + # Force weather update + weatherLastUpdate=0 + save_config + return 0 + else + yad --title "Location Error" --text="Could not find location: $newLocation" --button=gtk-ok + return 1 + fi + fi + return 1 +} + +# Display weather information in a text-info dialog +display_weather() { + local lastUpdateText="Never updated" + [[ "$weatherLastUpdate" -gt 0 ]] && lastUpdateText="Last updated: $(time_diff "$weatherLastUpdate")" + + # Create the weather information text with proper line breaks + weatherInfoText="Weather for $cityName +$lastUpdateText + +Current Conditions +Temperature: ${currentTemp}° F +Conditions: $currentConditions +Humidity: ${currentHumidity}% +Wind Speed: ${currentWindSpeedMph} mph + +3-Day Forecast +──────────────────────────────────── +${forecastFormattedDates[0]} +Temp: ${forecastMinTemps[0]}° to ${forecastMaxTemps[0]}° F +Conditions: ${forecastConditions[0]} +──────────────────────────────────── +${forecastFormattedDates[1]} +Temp: ${forecastMinTemps[1]}° to ${forecastMaxTemps[1]}° F +Conditions: ${forecastConditions[1]} +──────────────────────────────────── +${forecastFormattedDates[2]} +Temp: ${forecastMinTemps[2]}° to ${forecastMaxTemps[2]}° F +Conditions: ${forecastConditions[2]} + +End of text. Press Control+Home to return to the beginning." + + # Display in text-info dialog for screen reader accessibility + echo "$weatherInfoText" | yad --pname=I38Weather \ + --title="I38 Weather Monitor" \ + --text-info \ + --show-cursor \ + --width=500 \ + --height=600 \ + --center \ + --button="Settings:$settingsBtn" \ + --button="Refresh:$refreshBtn" \ + --button="Close:$quitBtn" + + return $? +} + +# Function to display settings dialog +display_settings() { + local ret=$(yad --pname=I38WeatherSettings \ + --title="I38 Weather Settings" \ + --form \ + --width=400 \ + --center \ + --field="Location:":TEXT "$cityName" \ + --field="Current Coordinates:":LBL "Lat: $latitude, Lon: $longitude" \ + --field="Temperature Unit:":CB "F!C" \ + --field="Update Interval (minutes):":NUM "$updateInterval!5..120!5" \ + --button="Cancel:1" \ + --button="Save:0") + + local saveResult=$? + + if [[ $saveResult -eq 0 && -n "$ret" ]]; then + local newLocation=$(echo "$ret" | cut -d"|" -f1) + local newUnit=$(echo "$ret" | cut -d"|" -f3) + local newInterval=$(echo "$ret" | cut -d"|" -f4) + + # Apply any changes + [[ -n "$newLocation" && "$newLocation" != "$cityName" ]] && change_location "$newLocation" + [[ -n "$newUnit" && "$newUnit" != "$tempUnit" ]] && tempUnit="$newUnit" && save_config + [[ -n "$newInterval" && "$newInterval" != "$updateInterval" ]] && updateInterval="$newInterval" && save_config + fi +} + +# Main loop +while : ; do + get_location + fetch_weather_data + + # Display weather using the text-info widget + display_weather + ret=$? + + # Handle button actions + case $ret in + $refreshBtn) + # Force a weather update + weatherLastUpdate=0 + continue + ;; + $settingsBtn) + # Display settings dialog + display_settings + continue + ;; + $quitBtn|252) + # Quit button or window closed + break + ;; + esac +done + +exit 0