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.
This commit is contained in:
parent
8064eaa6e2
commit
dc8f832840
566
I38.html
566
I38.html
@ -1,566 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="generator" content="pandoc" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
|
||||||
<title>Welcome to I38</title>
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
color: #1a1a1a;
|
|
||||||
background-color: #fdfdfd;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 36em;
|
|
||||||
padding-left: 50px;
|
|
||||||
padding-right: 50px;
|
|
||||||
padding-top: 50px;
|
|
||||||
padding-bottom: 50px;
|
|
||||||
hyphens: auto;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-kerning: normal;
|
|
||||||
}
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
body {
|
|
||||||
font-size: 0.9em;
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media print {
|
|
||||||
html {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
background-color: transparent;
|
|
||||||
color: black;
|
|
||||||
font-size: 12pt;
|
|
||||||
}
|
|
||||||
p, h2, h3 {
|
|
||||||
orphans: 3;
|
|
||||||
widows: 3;
|
|
||||||
}
|
|
||||||
h2, h3, h4 {
|
|
||||||
page-break-after: avoid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
a:visited {
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
svg {
|
|
||||||
height: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
margin-top: 1.4em;
|
|
||||||
}
|
|
||||||
h5, h6 {
|
|
||||||
font-size: 1em;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
h6 {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
ol, ul {
|
|
||||||
padding-left: 1.7em;
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
li > ol, li > ul {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
margin: 1em 0 1em 1.7em;
|
|
||||||
padding-left: 1em;
|
|
||||||
border-left: 2px solid #e6e6e6;
|
|
||||||
color: #606060;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
|
|
||||||
font-size: 85%;
|
|
||||||
margin: 0;
|
|
||||||
hyphens: manual;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
margin: 1em 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
pre code {
|
|
||||||
padding: 0;
|
|
||||||
overflow: visible;
|
|
||||||
overflow-wrap: normal;
|
|
||||||
}
|
|
||||||
.sourceCode {
|
|
||||||
background-color: transparent;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid #1a1a1a;
|
|
||||||
height: 1px;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
margin: 1em 0;
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
overflow-x: auto;
|
|
||||||
display: block;
|
|
||||||
font-variant-numeric: lining-nums tabular-nums;
|
|
||||||
}
|
|
||||||
table caption {
|
|
||||||
margin-bottom: 0.75em;
|
|
||||||
}
|
|
||||||
tbody {
|
|
||||||
margin-top: 0.5em;
|
|
||||||
border-top: 1px solid #1a1a1a;
|
|
||||||
border-bottom: 1px solid #1a1a1a;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
border-top: 1px solid #1a1a1a;
|
|
||||||
padding: 0.25em 0.5em 0.25em 0.5em;
|
|
||||||
}
|
|
||||||
td {
|
|
||||||
padding: 0.125em 0.5em 0.25em 0.5em;
|
|
||||||
}
|
|
||||||
header {
|
|
||||||
margin-bottom: 4em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#TOC li {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
#TOC ul {
|
|
||||||
padding-left: 1.3em;
|
|
||||||
}
|
|
||||||
#TOC > ul {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
#TOC a:not(:hover) {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
code{white-space: pre-wrap;}
|
|
||||||
span.smallcaps{font-variant: small-caps;}
|
|
||||||
div.columns{display: flex; gap: min(4vw, 1.5em);}
|
|
||||||
div.column{flex: auto; overflow-x: auto;}
|
|
||||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
|
||||||
/* The extra [class] is a hack that increases specificity enough to
|
|
||||||
override a similar rule in reveal.js */
|
|
||||||
ul.task-list[class]{list-style: none;}
|
|
||||||
ul.task-list li input[type="checkbox"] {
|
|
||||||
font-size: inherit;
|
|
||||||
width: 0.8em;
|
|
||||||
margin: 0 0.8em 0.2em -1.6em;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header id="title-block-header">
|
|
||||||
<h1 class="title">Welcome to I38</h1>
|
|
||||||
</header>
|
|
||||||
<h1 id="welcome-to-i38---accessible-i3-window-manager">Welcome to I38 -
|
|
||||||
Accessible i3 Window Manager</h1>
|
|
||||||
<blockquote>
|
|
||||||
<p><strong>Note:</strong> This help guide has been tailored to your
|
|
||||||
specific configuration. You’ve chosen <strong>BROWSER</strong> as your
|
|
||||||
web browser, <strong>MODKEY</strong> as your mod key, and you’re using
|
|
||||||
the <strong>SCREENREADER</strong> screen reader.</p>
|
|
||||||
</blockquote>
|
|
||||||
<h2 id="introduction-to-i38">Introduction to I38</h2>
|
|
||||||
<p>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.</p>
|
|
||||||
<p>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.</p>
|
|
||||||
<h3 id="coming-from-gnome-or-mate">Coming from GNOME or MATE?</h3>
|
|
||||||
<p>If you’re transitioning from GNOME or MATE, here are some key
|
|
||||||
differences to understand:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Window Management</strong>: 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.</li>
|
|
||||||
<li><strong>Panels and Indicators</strong>: Instead of persistent panels
|
|
||||||
with menus and indicators, I38 uses keyboard shortcuts to access
|
|
||||||
functionality.</li>
|
|
||||||
<li><strong>Workspace Navigation</strong>: 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.</li>
|
|
||||||
<li><strong>Application Launching</strong>: Rather than using a start
|
|
||||||
menu or activities overview, I38 provides keyboard shortcuts for
|
|
||||||
launching applications.</li>
|
|
||||||
</ul>
|
|
||||||
<p>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.</p>
|
|
||||||
<h2 id="basic-concepts">Basic Concepts</h2>
|
|
||||||
<h3 id="workspaces">Workspaces</h3>
|
|
||||||
<p>Workspaces act like virtual desktops, allowing you to organize
|
|
||||||
applications. You have 10 workspaces available.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Switch to workspace: <code>Control</code> + <code>F1</code> through
|
|
||||||
<code>F10</code></li>
|
|
||||||
<li>Move window to workspace: <code>Control</code> + <code>Shift</code>
|
|
||||||
+ <code>F1</code> through <code>F10</code></li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Similar to workspaces in GNOME/MATE,
|
|
||||||
but with dedicated keyboard shortcuts rather than overview modes or
|
|
||||||
workspace switchers.</p>
|
|
||||||
<h3 id="window-management">Window Management</h3>
|
|
||||||
<p>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.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Switch between windows: <code>Alt</code> + <code>Tab</code> (next)
|
|
||||||
or <code>Alt</code> + <code>Shift</code> + <code>Tab</code>
|
|
||||||
(previous)</li>
|
|
||||||
<li>Launch terminal: <code>MODKEY</code> + <code>Return</code></li>
|
|
||||||
<li>Close window: <code>MODKEY</code> + <code>F4</code></li>
|
|
||||||
<li>Toggle fullscreen: <code>MODKEY</code> + <code>BackSpace</code></li>
|
|
||||||
<li>List windows in current workspace: <code>RATPOISONKEY</code> then
|
|
||||||
<code>'</code> (apostrophe)</li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Alt+Tab works similarly to
|
|
||||||
GNOME/MATE, but window placement is automatic rather than manual.</p>
|
|
||||||
<h2 id="modes-in-i38">Modes in I38</h2>
|
|
||||||
<h3 id="default-mode">Default Mode</h3>
|
|
||||||
<p>This is the standard mode for working with applications. Most
|
|
||||||
commands start with your mod key (<code>MODKEY</code>).</p>
|
|
||||||
<h3 id="ratpoison-mode">Ratpoison Mode</h3>
|
|
||||||
<p>Ratpoison mode allows quick access to common actions using shorter
|
|
||||||
key combinations. To enter Ratpoison mode, press
|
|
||||||
<code>RATPOISONKEY</code>. After pressing this key, you can execute
|
|
||||||
commands with single keystrokes.</p>
|
|
||||||
<p>Common Ratpoison mode commands:</p>
|
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col style="width: 38%" />
|
|
||||||
<col style="width: 61%" />
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Key</th>
|
|
||||||
<th>Action</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><code>c</code></td>
|
|
||||||
<td>Launch a terminal</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>e</code></td>
|
|
||||||
<td>Open text editor (TEXTEDITOR)</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>w</code></td>
|
|
||||||
<td>Launch web browser (BROWSER)</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>k</code></td>
|
|
||||||
<td>Kill (close) the current window</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>?</code></td>
|
|
||||||
<td>Show I38 help</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>Escape</code> or <code>Control</code> + <code>g</code></td>
|
|
||||||
<td>Exit Ratpoison mode without taking action</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>Shift</code> + <code>c</code></td>
|
|
||||||
<td>Restart Cthulhu screen reader</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>Shift</code> + <code>o</code></td>
|
|
||||||
<td>Restart Orca screen reader</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>Shift</code> + <code>t</code></td>
|
|
||||||
<td>Toggle screen reader</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>Control</code> + <code>;</code></td>
|
|
||||||
<td>Reload I38 configuration</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>Control</code> + <code>q</code></td>
|
|
||||||
<td>Exit i3 (log out)</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>!</code></td>
|
|
||||||
<td>Open run dialog</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>Alt</code> + <code>b</code></td>
|
|
||||||
<td>Check battery status</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>g</code></td>
|
|
||||||
<td>Check game controller status</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> This mode has no direct equivalent in
|
|
||||||
GNOME/MATE. Think of it as a command palette or quick launcher activated
|
|
||||||
by a single key.</p>
|
|
||||||
<h3 id="bypass-mode">Bypass Mode</h3>
|
|
||||||
<p>Bypass mode passes all keys directly to the application, which is
|
|
||||||
useful for applications that need many keyboard shortcuts. To enter
|
|
||||||
bypass mode, press <code>MODKEY</code> + <code>Shift</code> +
|
|
||||||
<code>BackSpace</code>. Use the same key combination to exit bypass
|
|
||||||
mode.</p>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> In GNOME/MATE, applications always
|
|
||||||
receive keyboard input directly. Bypass mode simulates this behavior
|
|
||||||
within i3.</p>
|
|
||||||
<h2 id="accessibility-features">Accessibility Features</h2>
|
|
||||||
<h3 id="screen-reader">Screen Reader</h3>
|
|
||||||
<p>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.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Toggle screen reader: <code>RATPOISONKEY</code> then
|
|
||||||
<code>Shift</code> + <code>t</code></li>
|
|
||||||
<li>Restart screen reader: <code>RATPOISONKEY</code> then
|
|
||||||
<code>Shift</code> + <code>o</code> (for Orca) or <code>Shift</code> +
|
|
||||||
<code>c</code> (for Cthulhu)</li>
|
|
||||||
<li>Interrupt speech: <code>MODKEY</code> + <code>Shift</code> +
|
|
||||||
<code>F5</code></li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> GNOME uses Orca by default with its
|
|
||||||
own keyboard shortcuts. I38 integrates screen readers more deeply with
|
|
||||||
the window manager.</p>
|
|
||||||
<h3 id="braille-display-support">Braille Display Support</h3>
|
|
||||||
<p>If you’ve enabled braille display support during setup, I38 will
|
|
||||||
start XBrlAPI automatically to provide braille output from your screen
|
|
||||||
reader.</p>
|
|
||||||
<h3 id="ocr-optical-character-recognition">OCR (Optical Character
|
|
||||||
Recognition)</h3>
|
|
||||||
<p>If installed, you can use OCR to read text from images or
|
|
||||||
inaccessible applications:</p>
|
|
||||||
<ul>
|
|
||||||
<li><code>MODKEY</code> + <code>F5</code>: Perform OCR on the entire
|
|
||||||
screen and speak the content</li>
|
|
||||||
<li>In Ratpoison mode: <code>Print</code> or <code>MODKEY</code> +
|
|
||||||
<code>r</code>: Perform OCR and save to clipboard</li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> OCR features are typically not
|
|
||||||
integrated into GNOME/MATE by default.</p>
|
|
||||||
<h3 id="sound-effects">Sound Effects</h3>
|
|
||||||
<p>I38 provides audio feedback for many actions:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Window open/close: Ascending/descending tones</li>
|
|
||||||
<li>Mode changes: Distinctive sounds for each mode</li>
|
|
||||||
<li>Workspace changes: Subtle audio cues</li>
|
|
||||||
<li>Fullscreen toggle: Special sound effect</li>
|
|
||||||
</ul>
|
|
||||||
<p>This audio feedback provides non-visual confirmation of actions and
|
|
||||||
state changes.</p>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> GNOME/MATE typically have fewer sound
|
|
||||||
effects for window management actions.</p>
|
|
||||||
<h2 id="application-menu-and-running-programs">Application Menu and
|
|
||||||
Running Programs</h2>
|
|
||||||
<p>Access applications in multiple ways:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Applications menu: <code>MODKEY</code> + <code>F1</code></li>
|
|
||||||
<li>Run dialog (enter a command): <code>MODKEY</code> + <code>F2</code>
|
|
||||||
or in Ratpoison mode, <code>!</code> (exclamation mark)</li>
|
|
||||||
<li>Common applications have dedicated shortcuts in Ratpoison mode (see
|
|
||||||
table above)</li>
|
|
||||||
</ul>
|
|
||||||
<p>The applications menu is organized by categories similar to
|
|
||||||
traditional desktop environments.</p>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Instead of clicking on application
|
|
||||||
icons or using a start menu, I38 provides keyboard shortcuts to access
|
|
||||||
applications.</p>
|
|
||||||
<h2 id="reminders-and-notifications">Reminders and Notifications</h2>
|
|
||||||
<p>I38 includes integration with the <code>remind</code> program for
|
|
||||||
managing reminders:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Access the reminder tool: <code>RATPOISONKEY</code> then
|
|
||||||
<code>r</code></li>
|
|
||||||
<li>Create various types of reminders (one-time, daily, weekly,
|
|
||||||
monthly)</li>
|
|
||||||
<li>Get notification alerts for your reminders</li>
|
|
||||||
</ul>
|
|
||||||
<p>The reminder tool provides the following features:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>One-time Reminders</strong>: Set for a specific date and
|
|
||||||
time</li>
|
|
||||||
<li><strong>Daily Reminders</strong>: Occur every day at the specified
|
|
||||||
time</li>
|
|
||||||
<li><strong>Weekly Reminders</strong>: Occur on specific days of the
|
|
||||||
week</li>
|
|
||||||
<li><strong>Monthly Reminders</strong>: Occur on a specific day each
|
|
||||||
month or the last day of each month</li>
|
|
||||||
<li><strong>Custom Reminders</strong>: Create complex reminder
|
|
||||||
patterns</li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Similar to calendar applications in
|
|
||||||
GNOME/MATE but with a simplified interface optimized for keyboard
|
|
||||||
navigation.</p>
|
|
||||||
<h2 id="volume-and-media-controls">Volume and Media Controls</h2>
|
|
||||||
<h3 id="system-volume">System Volume</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Increase volume: <code>MODKEY</code> +
|
|
||||||
<code>XF86AudioRaiseVolume</code></li>
|
|
||||||
<li>Decrease volume: <code>MODKEY</code> +
|
|
||||||
<code>XF86AudioLowerVolume</code></li>
|
|
||||||
<li>Mute/unmute: <code>MODKEY</code> + <code>XF86AudioMute</code></li>
|
|
||||||
</ul>
|
|
||||||
<h3 id="media-player-controls">Media Player Controls</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Play/Pause: <code>XF86AudioPlay</code></li>
|
|
||||||
<li>Next track: <code>XF86AudioNext</code></li>
|
|
||||||
<li>Previous track: <code>XF86AudioPrev</code></li>
|
|
||||||
<li>Stop: <code>XF86AudioStop</code></li>
|
|
||||||
<li>Media info: <code>MODKEY</code> + <code>XF86AudioPlay</code></li>
|
|
||||||
</ul>
|
|
||||||
<p>In Ratpoison mode, these are also available with Alt+Shift
|
|
||||||
combinations:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Increase volume: <code>Alt</code> + <code>Shift</code> +
|
|
||||||
<code>=</code></li>
|
|
||||||
<li>Decrease volume: <code>Alt</code> + <code>Shift</code> +
|
|
||||||
<code>-</code></li>
|
|
||||||
<li>Previous track: <code>Alt</code> + <code>Shift</code> +
|
|
||||||
<code>z</code></li>
|
|
||||||
<li>Pause: <code>Alt</code> + <code>Shift</code> + <code>c</code></li>
|
|
||||||
<li>Play: <code>Alt</code> + <code>Shift</code> + <code>x</code></li>
|
|
||||||
<li>Stop: <code>Alt</code> + <code>Shift</code> + <code>v</code></li>
|
|
||||||
<li>Next track: <code>Alt</code> + <code>Shift</code> +
|
|
||||||
<code>b</code></li>
|
|
||||||
<li>Media info: <code>Alt</code> + <code>Shift</code> +
|
|
||||||
<code>u</code></li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Media controls are similar to those
|
|
||||||
in GNOME/MATE, with the addition of audio feedback.</p>
|
|
||||||
<h2 id="file-management">File Management</h2>
|
|
||||||
<p>I38 uses FILEBROWSER for file management. Launch it in Ratpoison mode
|
|
||||||
with the <code>f</code> key.</p>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Similar functionality to Nautilus
|
|
||||||
(GNOME) or Caja (MATE), but launched via keyboard shortcut rather than
|
|
||||||
from a desktop icon or menu.</p>
|
|
||||||
<h2 id="system-operations">System Operations</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Reload I38 configuration: In Ratpoison mode, <code>Control</code> +
|
|
||||||
<code>;</code> (semicolon)</li>
|
|
||||||
<li>Exit i3 (log out): In Ratpoison mode, <code>Control</code> +
|
|
||||||
<code>q</code></li>
|
|
||||||
<li>Check battery status: In Ratpoison mode, <code>Alt</code> +
|
|
||||||
<code>b</code></li>
|
|
||||||
<li>Check game controller status: In Ratpoison mode, <code>g</code></li>
|
|
||||||
<li>Adjust screen brightness (if xrandr is available): In Ratpoison
|
|
||||||
mode, <code>Alt</code> + <code>s</code></li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> These functions are typically
|
|
||||||
available through system menus or indicators in GNOME/MATE.</p>
|
|
||||||
<h2 id="keyboard-layouts">Keyboard Layouts</h2>
|
|
||||||
<p>Switch between layouts: <code>Super</code> + <code>Space</code></p>
|
|
||||||
<p>This is only available if you chose multiple keyboard layouts during
|
|
||||||
setup.</p>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Similar to keyboard layout switching
|
|
||||||
in GNOME/MATE, but with a different default shortcut.</p>
|
|
||||||
<h2 id="desktop-and-window-decorations">Desktop and Window
|
|
||||||
Decorations</h2>
|
|
||||||
<p>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.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Show desktop icons: <code>MODKEY</code> + <code>Control</code> +
|
|
||||||
<code>d</code></li>
|
|
||||||
</ul>
|
|
||||||
<h2 id="clipboard-management">Clipboard Management</h2>
|
|
||||||
<p>I38 includes clipboard management features:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Access clipboard history: <code>MODKEY</code> + <code>Control</code>
|
|
||||||
+ <code>c</code></li>
|
|
||||||
</ul>
|
|
||||||
<h2 id="bookmark-management">Bookmark Management</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Access bookmarks: <code>MODKEY</code> + <code>Control</code> +
|
|
||||||
<code>b</code></li>
|
|
||||||
</ul>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Bookmarks are typically managed
|
|
||||||
within applications in GNOME/MATE. I38 provides a system-wide bookmark
|
|
||||||
manager.</p>
|
|
||||||
<h2 id="tips-for-new-users">Tips for New Users</h2>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Start with Ratpoison mode</strong>: Learn the single-key
|
|
||||||
commands first, as they’re easier to remember.</li>
|
|
||||||
<li><strong>Use the window list</strong>: When you’re lost, use
|
|
||||||
<code>RATPOISONKEY</code> then <code>'</code> to show all windows in the
|
|
||||||
current workspace.</li>
|
|
||||||
<li><strong>Bookmark important websites</strong>: Use
|
|
||||||
<code>MODKEY</code> + <code>Control</code> + <code>b</code> to access
|
|
||||||
bookmarks.</li>
|
|
||||||
<li><strong>Remember the help shortcut</strong>: <code>MODKEY</code> +
|
|
||||||
<code>Shift</code> + <code>F1</code> is your friend when you need
|
|
||||||
guidance.</li>
|
|
||||||
<li><strong>Let the sound effects guide you</strong>: Pay attention to
|
|
||||||
the audio cues to understand what’s happening.</li>
|
|
||||||
<li><strong>Take advantage of OCR</strong>: If an application isn’t
|
|
||||||
accessible, try the OCR function.</li>
|
|
||||||
</ul>
|
|
||||||
<h3 id="transitioning-from-gnomemate">Transitioning from GNOME/MATE</h3>
|
|
||||||
<ul>
|
|
||||||
<li>Start by learning the basic navigation shortcuts before exploring
|
|
||||||
advanced features</li>
|
|
||||||
<li>The tabbed layout should feel somewhat familiar if you’re used to
|
|
||||||
browser tabs</li>
|
|
||||||
<li>Alt+Tab works similarly to GNOME/MATE for switching between
|
|
||||||
windows</li>
|
|
||||||
<li>Focus on keyboard commands rather than looking for visual elements
|
|
||||||
like panels or docks</li>
|
|
||||||
</ul>
|
|
||||||
<h2 id="customization">Customization</h2>
|
|
||||||
<p>You can customize I38 by editing the file
|
|
||||||
<code>~/.config/i3/customizations</code>. This file will not be
|
|
||||||
overwritten when you update I38.</p>
|
|
||||||
<p>Example customizations:</p>
|
|
||||||
<pre><code># 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%</code></pre>
|
|
||||||
<p>To reconfigure I38 completely, run the <code>i38.sh</code> script
|
|
||||||
again.</p>
|
|
||||||
<p><em>GNOME/MATE comparison:</em> Much more text-based configuration
|
|
||||||
compared to the graphical settings dialogs in GNOME/MATE.</p>
|
|
||||||
<h2 id="getting-help">Getting Help</h2>
|
|
||||||
<p>If you need assistance with I38, you can:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Press <code>MODKEY</code> + <code>Shift</code> + <code>F1</code> to
|
|
||||||
view the help documentation</li>
|
|
||||||
<li>Visit the Stormux website at <a
|
|
||||||
href="https://stormux.org">stormux.org</a></li>
|
|
||||||
<li>Check the i3 documentation at <a
|
|
||||||
href="https://i3wm.org/docs/userguide.html">i3wm.org/docs/userguide.html</a></li>
|
|
||||||
</ul>
|
|
||||||
<hr />
|
|
||||||
<p><em>I38 - Making i3 accessible. A Stormux project. License: GPL
|
|
||||||
v3</em></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
265
I38.md
Normal file
265
I38.md
Normal file
@ -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*
|
@ -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
|
- notification-daemon: To handle notifications
|
||||||
- xfce4-notifyd: For sending notifications Replaces notification-daemon (Sway user will need to install the customized variant at <https://github.com/icasdri/xfce4-notifyd-layer-shell>)
|
- xfce4-notifyd: For sending notifications Replaces notification-daemon (Sway user will need to install the customized variant at <https://github.com/icasdri/xfce4-notifyd-layer-shell>)
|
||||||
- ocrdesktop: For getting contents of the current window with OCR.
|
- ocrdesktop: For getting contents of the current window with OCR.
|
||||||
|
- pandoc or markdown: To generate html files.
|
||||||
- pamixer: for the mute-unmute script
|
- pamixer: for the mute-unmute script
|
||||||
- pcmanfm: [optional] Graphical file manager.
|
- pcmanfm: [optional] Graphical file manager.
|
||||||
- playerctl: music controls
|
- playerctl: music controls
|
||||||
|
60
i38.sh
60
i38.sh
@ -33,6 +33,11 @@ if [[ -n "${missing}" ]]; then
|
|||||||
echo "${missing[*]}"
|
echo "${missing[*]}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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() {
|
keyboard_menu() {
|
||||||
keyboardMenu=("us" "English (US)"
|
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+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
|
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
|
# 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+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"
|
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
|
echo "bindsym Mod4+space exec ${i3Path}/scripts/keyboard.sh cycle ${kbd[@]}" >> ${i3Path}/config
|
||||||
fi
|
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.
|
# Create ratpoison mode if requested.
|
||||||
if [[ -n "${escapeKey}" ]]; then
|
if [[ -n "${escapeKey}" ]]; then
|
||||||
cat << EOF >> ${i3Path}/config
|
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
|
#Check battery status
|
||||||
bindsym Mod1+b exec --no-startup-id ${i3Path}/scripts/battery_status.sh, mode "default"
|
bindsym Mod1+b exec --no-startup-id ${i3Path}/scripts/battery_status.sh, mode "default"
|
||||||
#Check controller battery status
|
#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
|
# Get a list of windows in the current workspace
|
||||||
bindsym apostrophe exec --no-startup-id ${i3Path}/scripts/window_list.sh, mode "default"
|
bindsym apostrophe exec --no-startup-id ${i3Path}/scripts/window_list.sh, mode "default"
|
||||||
# Restart Cthulhu
|
# Restart Cthulhu
|
||||||
@ -676,7 +695,7 @@ fi
|
|||||||
|
|
||||||
cat << EOF >> ${i3Path}/config
|
cat << EOF >> ${i3Path}/config
|
||||||
# Auto start section
|
# Auto start section
|
||||||
$(if [[ $sounzzds -eq 0 ]]; then
|
$(if [[ $sounds -eq 0 ]]; then
|
||||||
if [[ $usingSway -eq 0 ]]; then
|
if [[ $usingSway -eq 0 ]]; then
|
||||||
echo "exec --no-startup-id ${i3Path}/scripts/sound.py"
|
echo "exec --no-startup-id ${i3Path}/scripts/sound.py"
|
||||||
else
|
else
|
||||||
@ -725,19 +744,34 @@ fi)
|
|||||||
# First run help documentation
|
# First run help documentation
|
||||||
exec --no-startup-id bash -c 'if [[ -f "${i3Path}/firstrun" ]]; then ${webBrowser} "${i3Path}/I38.html"& rm "${i3Path}/firstrun"; fi'
|
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
|
# If you want to add personal customizations to i3, add them in ${i3Path}/customizations
|
||||||
# It is not overwritten when the config file is recreated.
|
# It is not overwritten when the config file is recreated.
|
||||||
include "${i3Path}/customizations"
|
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
|
EOF
|
||||||
touch "${i3Path}/customizations"
|
touch "${i3Path}/customizations"
|
||||||
cp -v panel.conf "${i3Path}/panel.conf"
|
# Check for markdown or pandoc for converting the welcome document
|
||||||
# Move html help file to destination
|
if command -v pandoc &> /dev/null ; then
|
||||||
cp -v I38.html "${i3Path}/I38.html"
|
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"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Welcome to I38</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
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"
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
# More readable version of variables.
|
# More readable version of variables.
|
||||||
escapeKey="${escapeKey//Mod1/Alt}"
|
escapeKey="${escapeKey//Mod1/Alt}"
|
||||||
|
110
scripts/sysinfo.sh
Executable file
110
scripts/sysinfo.sh
Executable file
@ -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
|
439
scripts/weather.sh
Executable file
439
scripts/weather.sh
Executable file
@ -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
|
Loading…
x
Reference in New Issue
Block a user