Compare commits

...

11 Commits

Author SHA1 Message Date
44e53f19b5 Add Pi 3 serial baud rate fix instructions 2026-03-13 20:14:14 -06:00
dee45e5eae Expand lines 2026-03-04 17:31:14 -07:00
eaaa8e5f57 Ignore aider 2026-03-04 17:31:06 -07:00
a19aec1848 refactor: Expand single-line statements for improved readability
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-04 17:27:17 -07:00
af6072627e fix: Clear screen after printing labels and forum searches
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-04 17:20:11 -07:00
339a58e1bf Fix: Ensure accurate typing state and simplify entry key check
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-04 17:13:25 -07:00
3fc74d82ad fix: Prevent typing debounce on Nametag and Label screens
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-04 17:10:11 -07:00
17e3ad347a fix: Disable key debounce for text input entry and typing
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-04 17:03:58 -07:00
c58a356c02 fix: Clear line artifacts in Nametag, Label, Message, and Think screens
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-04 16:47:49 -07:00
d8886e80db chore: Add logging import statement 2026-03-04 16:47:47 -07:00
fdfefffda6 fix: Prevent key buffering during rate limiting
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
2026-03-04 16:37:38 -07:00
4 changed files with 395 additions and 135 deletions

1
.gitignore vendored
View File

@@ -115,3 +115,4 @@ venv.bak/
secrets.py secrets.py
tmp.png tmp.png
.aider*

View File

@@ -215,6 +215,12 @@ Append to `config.txt`:
enable_uart=1 enable_uart=1
``` ```
On Raspberry Pi 3's you'll also need to append:
```
core_freq=250
```
Optionally set up a cronjob: Optionally set up a cronjob:
``` ```

15
main.py
View File

@@ -43,15 +43,16 @@ def main_loop(stdscr):
logging.info('Starting main loop...') logging.info('Starting main loop...')
while True: while True:
# Rate limit key presses unless in a text input field c = stdscr.getch()
if not screens[state.current_screen].is_typing and time.time() < state.last_key_time + 1: if c != curses.ERR:
time.sleep(0.05) current_screen = screens[state.current_screen]
else: # Rate limit key presses unless in a text input field
c = stdscr.getch() if not current_screen.is_typing and not current_screen.is_entry_key(c) and time.time() < state.last_key_time + 1:
if c != curses.ERR: pass # Key press ignored due to rate limit
else:
state.c = c state.c = c
state.last_key_time = time.time() state.last_key_time = time.time()
screens[state.current_screen].handle_input(c) current_screen.handle_input(c)
if state.current_screen != state.prev_screen: if state.current_screen != state.prev_screen:
logging.info('Switching to screen: %s', state.current_screen) logging.info('Switching to screen: %s', state.current_screen)

508
tui.py
View File

@@ -3,6 +3,7 @@ import time
import textwrap import textwrap
import random import random
import os import os
import logging
from datetime import datetime, timezone from datetime import datetime, timezone
import utils import utils
@@ -43,6 +44,9 @@ class Screen:
except: except:
return None return None
def is_entry_key(self, c):
return False
def try_highlight(self, c): def try_highlight(self, c):
if c and time.time() - self.state.highlight_debounce > 0.6: if c and time.time() - self.state.highlight_debounce > 0.6:
self.state.highlight_debounce = time.time() self.state.highlight_debounce = time.time()
@@ -123,22 +127,38 @@ class HomeScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 's': self.state.current_screen = 'stats' if button == 's':
elif button == 'i': self.state.current_screen = 'info' self.state.current_screen = 'stats'
elif button == 'n': self.state.current_screen = 'nametag' elif button == 'i':
elif button == 'l': self.state.current_screen = 'label' self.state.current_screen = 'info'
elif button == 'z': self.state.current_screen = 'games' elif button == 'n':
elif button == '0': self.state.current_screen = 'asimov' self.state.current_screen = 'nametag'
elif button == 'g': self.state.current_screen = 'sign' elif button == 'l':
elif button == 'v': self.state.current_screen = 'protovac_sign' self.state.current_screen = 'label'
elif button == 'r': self.state.current_screen = 'train' elif button == 'z':
elif button == 'c': self.state.current_screen = 'classes' self.state.current_screen = 'games'
elif button == 'm' and utils.openai_key: self.state.current_screen = 'message' elif button == '0':
elif button == 't' and utils.wa_api_key: self.state.current_screen = 'think' self.state.current_screen = 'asimov'
elif c == 68: self.state.current_screen = 'debug' elif button == 'g':
elif button == 'a': self.state.current_screen = 'about' self.state.current_screen = 'sign'
elif button == 'p': self.state.current_screen = 'protocoin' elif button == 'v':
else: self.try_highlight(c) self.state.current_screen = 'protovac_sign'
elif button == 'r':
self.state.current_screen = 'train'
elif button == 'c':
self.state.current_screen = 'classes'
elif button == 'm' and utils.openai_key:
self.state.current_screen = 'message'
elif button == 't' and utils.wa_api_key:
self.state.current_screen = 'think'
elif c == 68:
self.state.current_screen = 'debug'
elif button == 'a':
self.state.current_screen = 'about'
elif button == 'p':
self.state.current_screen = 'protocoin'
else:
self.try_highlight(c)
class DebugScreen(Screen): class DebugScreen(Screen):
@@ -153,8 +173,10 @@ class DebugScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
elif c == 88: exit() self.state.current_screen = 'home'
elif c == 88:
exit()
elif c == 83: elif c == 83:
curses.nocbreak() curses.nocbreak()
self.stdscr.keypad(False) self.stdscr.keypad(False)
@@ -171,7 +193,8 @@ class DebugScreen(Screen):
logging.info('Spawning nethack.') logging.info('Spawning nethack.')
os.system('/usr/games/nethack') os.system('/usr/games/nethack')
exit() exit()
else: self.try_highlight(c) else:
self.try_highlight(c)
class StatsScreen(Screen): class StatsScreen(Screen):
@@ -206,8 +229,10 @@ class StatsScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
else: self.try_highlight(c) self.state.current_screen = 'home'
else:
self.try_highlight(c)
class ClassesScreen(Screen): class ClassesScreen(Screen):
@@ -251,7 +276,8 @@ class ClassesScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
self.state.current_screen = 'home'
elif button == 'j' or c == curses.KEY_DOWN or c == KEY_SPACE: elif button == 'j' or c == curses.KEY_DOWN or c == KEY_SPACE:
if self.classes and self.classes_start+6 < len(self.classes): if self.classes and self.classes_start+6 < len(self.classes):
self.classes_start += 6 self.classes_start += 6
@@ -260,7 +286,8 @@ class ClassesScreen(Screen):
if self.classes_start > 0: if self.classes_start > 0:
self.classes_start -= 6 self.classes_start -= 6
self.stdscr.erase() self.stdscr.erase()
else: self.try_highlight(c) else:
self.try_highlight(c)
class TextScreen(Screen): class TextScreen(Screen):
def __init__(self, state, stdscr, title, text_content): def __init__(self, state, stdscr, title, text_content):
@@ -283,7 +310,8 @@ class TextScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
self.state.current_screen = 'home'
elif button == 'j' or c == curses.KEY_DOWN or c == KEY_SPACE: elif button == 'j' or c == curses.KEY_DOWN or c == KEY_SPACE:
if self.text_line+19 < len(self.lines): if self.text_line+19 < len(self.lines):
self.text_line += 19 self.text_line += 19
@@ -292,7 +320,8 @@ class TextScreen(Screen):
if self.text_line > 0: if self.text_line > 0:
self.text_line -= 19 self.text_line -= 19
self.stdscr.erase() self.stdscr.erase()
else: self.try_highlight(c) else:
self.try_highlight(c)
class ProtocoinScreen(Screen): class ProtocoinScreen(Screen):
def __init__(self, state, stdscr): def __init__(self, state, stdscr):
@@ -334,7 +363,8 @@ class ProtocoinScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
self.state.current_screen = 'home'
elif button == 'j' or c == curses.KEY_DOWN or c == KEY_SPACE: elif button == 'j' or c == curses.KEY_DOWN or c == KEY_SPACE:
if self.protocoin and self.protocoin_line+19 < len(self.protocoin['transactions']): if self.protocoin and self.protocoin_line+19 < len(self.protocoin['transactions']):
self.protocoin_line += 19 self.protocoin_line += 19
@@ -343,7 +373,8 @@ class ProtocoinScreen(Screen):
if self.protocoin_line > 0: if self.protocoin_line > 0:
self.protocoin_line -= 19 self.protocoin_line -= 19
self.stdscr.erase() self.stdscr.erase()
else: self.try_highlight(c) else:
self.try_highlight(c)
class SignScreen(Screen): class SignScreen(Screen):
def __init__(self, state, stdscr): def __init__(self, state, stdscr):
@@ -387,11 +418,17 @@ class SignScreen(Screen):
self.sign_to_send = '' self.sign_to_send = ''
else: else:
self.sign_to_send = self.handle_text_input(c, self.sign_to_send) self.sign_to_send = self.handle_text_input(c, self.sign_to_send)
elif button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' elif button == 'b' or c == KEY_ESCAPE:
self.state.current_screen = 'home'
elif button == 'e': elif button == 'e':
self.is_typing = True self.is_typing = True
self.sign_to_send = '_' self.sign_to_send = '_'
else: self.try_highlight(c) else:
self.try_highlight(c)
def is_entry_key(self, c):
button = self.get_button(c)
return not self.is_typing and button == 'e'
class ProtovacSignScreen(Screen): class ProtovacSignScreen(Screen):
def draw(self): def draw(self):
@@ -419,20 +456,34 @@ class ProtovacSignScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
res = '' res = ''
button = self.get_button(c) button = self.get_button(c)
if button == '1': res = utils.protovac_sign_color([255,255,255]) if button == '1':
elif button == '2': res = utils.protovac_sign_color([255,150,150]) res = utils.protovac_sign_color([255,255,255])
elif button == '3': res = utils.protovac_sign_color([150,255,150]) elif button == '2':
elif button == '4': res = utils.protovac_sign_color([150,150,255]) res = utils.protovac_sign_color([255,150,150])
elif button == '5': res = utils.protovac_sign_color([255,50,255]) elif button == '3':
elif button == '6': res = utils.protovac_sign_color([random.randint(50,255),random.randint(50,255),random.randint(50,255)]) res = utils.protovac_sign_color([150,255,150])
elif button == 'q': res = utils.protovac_sign_effect(0) elif button == '4':
elif button == 'w': res = utils.protovac_sign_effect(2) res = utils.protovac_sign_color([150,150,255])
elif button == 'e': res = utils.protovac_sign_effect(49) elif button == '5':
elif button == 'r': res = utils.protovac_sign_effect(90) res = utils.protovac_sign_color([255,50,255])
elif button == 't': res = utils.protovac_sign_effect(89) elif button == '6':
elif button == 'y': res = utils.protovac_sign_effect(random.randint(3,90)) res = utils.protovac_sign_color([random.randint(50,255),random.randint(50,255),random.randint(50,255)])
elif button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' elif button == 'q':
else: self.try_highlight(c) res = utils.protovac_sign_effect(0)
elif button == 'w':
res = utils.protovac_sign_effect(2)
elif button == 'e':
res = utils.protovac_sign_effect(49)
elif button == 'r':
res = utils.protovac_sign_effect(90)
elif button == 't':
res = utils.protovac_sign_effect(89)
elif button == 'y':
res = utils.protovac_sign_effect(random.randint(3,90))
elif button == 'b' or c == KEY_ESCAPE:
self.state.current_screen = 'home'
else:
self.try_highlight(c)
if res == 'Error': if res == 'Error':
self.stdscr.addstr(21, 12, 'ERROR') self.stdscr.addstr(21, 12, 'ERROR')
@@ -491,21 +542,30 @@ class NametagScreen(Screen):
if self.nametag_member: if self.nametag_member:
self.stdscr.addstr(8, 4, 'Enter your name: ' + self.nametag_member) self.stdscr.addstr(8, 4, 'Enter your name: ' + self.nametag_member)
self.stdscr.clrtoeol()
self.stdscr.move(10, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel')
elif self.nametag_guest: elif self.nametag_guest:
self.stdscr.move(8, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(10, 4, 'Enter your name: ' + self.nametag_guest) self.stdscr.addstr(10, 4, 'Enter your name: ' + self.nametag_guest)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel')
else: else:
self.stdscr.addstr(8, 4, '[M] Member nametag', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(8, 4, '[M] Member nametag', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(10, 4, '[G] Guest nametag', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(10, 4, '[G] Guest nametag', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol() self.stdscr.clrtoeol()
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if self.nametag_member: if self.nametag_member:
self.is_typing = True if c == KEY_ESCAPE:
if c == KEY_ESCAPE: self.nametag_member = '' self.nametag_member = ''
self.is_typing = False
elif c == KEY_ENTER: elif c == KEY_ENTER:
if len(self.nametag_member) > 1: if len(self.nametag_member) > 1:
self.stdscr.addstr(15, 4, 'Printing...') self.stdscr.addstr(15, 4, 'Printing...')
@@ -513,10 +573,12 @@ class NametagScreen(Screen):
utils.print_nametag(self.nametag_member[:-1], guest=False) utils.print_nametag(self.nametag_member[:-1], guest=False)
self.nametag_member = '' self.nametag_member = ''
self.state.current_screen = 'home' self.state.current_screen = 'home'
else: self.nametag_member = self.handle_text_input(c, self.nametag_member) else:
self.nametag_member = self.handle_text_input(c, self.nametag_member)
elif self.nametag_guest: elif self.nametag_guest:
self.is_typing = True if c == KEY_ESCAPE:
if c == KEY_ESCAPE: self.nametag_guest = '' self.nametag_guest = ''
self.is_typing = False
elif c == KEY_ENTER: elif c == KEY_ENTER:
if len(self.nametag_guest) > 1: if len(self.nametag_guest) > 1:
self.stdscr.addstr(15, 4, 'Printing...') self.stdscr.addstr(15, 4, 'Printing...')
@@ -524,13 +586,24 @@ class NametagScreen(Screen):
utils.print_nametag(self.nametag_guest[:-1], guest=True) utils.print_nametag(self.nametag_guest[:-1], guest=True)
self.nametag_guest = '' self.nametag_guest = ''
self.state.current_screen = 'home' self.state.current_screen = 'home'
else: self.nametag_guest = self.handle_text_input(c, self.nametag_guest) else:
self.nametag_guest = self.handle_text_input(c, self.nametag_guest)
else: else:
self.is_typing = False self.is_typing = False
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
elif button == 'm': self.nametag_member = '_' self.state.current_screen = 'home'
elif button == 'g': self.nametag_guest = '_' elif button == 'm':
else: self.try_highlight(c) self.nametag_member = '_'
self.is_typing = True
elif button == 'g':
self.nametag_guest = '_'
self.is_typing = True
else:
self.try_highlight(c)
def is_entry_key(self, c):
button = self.get_button(c)
return not self.is_typing and button in ['m', 'g']
class LabelScreen(Screen): class LabelScreen(Screen):
# This screen is complex, breaking it down into sub-states # This screen is complex, breaking it down into sub-states
@@ -568,28 +641,57 @@ class LabelScreen(Screen):
# Drawing logic for different sub-screens # Drawing logic for different sub-screens
if self.sub_screen == 'menu': if self.sub_screen == 'menu':
self.stdscr.addstr(8, 4, '[T] Tool label', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(8, 4, '[T] Tool label', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(10, 4, '[S] Sheet material', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(10, 4, '[S] Sheet material', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(12, 4, '[G] Generic label', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(12, 4, '[G] Generic label', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(14, 4, '[F] Forum thread', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(14, 4, '[F] Forum thread', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0)
elif self.sub_screen == 'tool': elif self.sub_screen == 'tool':
self.stdscr.addstr(8, 4, 'Enter Wiki-ID tool number: ' + self.label_tool) self.stdscr.addstr(8, 4, 'Enter Wiki-ID tool number: ' + self.label_tool)
self.stdscr.clrtoeol()
for i in [10, 12, 14]:
self.stdscr.move(i, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel')
elif self.sub_screen == 'material_name': elif self.sub_screen == 'material_name':
self.stdscr.addstr(10, 4, 'Enter your name: ' + self.label_material_name) self.stdscr.addstr(10, 4, 'Enter your name: ' + self.label_material_name)
self.stdscr.clrtoeol()
for i in [8, 12, 14]:
self.stdscr.move(i, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Next [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Next [ESC] Cancel')
elif self.sub_screen == 'material_contact': elif self.sub_screen == 'material_contact':
self.stdscr.addstr(10, 4, 'Enter your name: ' + self.label_material_name[:-1]) self.stdscr.addstr(10, 4, 'Enter your name: ' + self.label_material_name[:-1])
self.stdscr.clrtoeol()
self.stdscr.addstr(12, 4, 'Enter your contact info: ' + self.label_material_contact) self.stdscr.addstr(12, 4, 'Enter your contact info: ' + self.label_material_contact)
self.stdscr.clrtoeol()
for i in [8, 14]:
self.stdscr.move(i, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel')
elif self.sub_screen == 'generic': elif self.sub_screen == 'generic':
self.stdscr.addstr(12, 4, 'Enter your message: ' + self.label_generic) self.stdscr.addstr(12, 4, 'Enter your message: ' + self.label_generic)
self.stdscr.clrtoeol()
for i in [8, 10, 14]:
self.stdscr.move(i, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel')
elif self.sub_screen == 'consumable': elif self.sub_screen == 'consumable':
self.stdscr.addstr(12, 4, 'Enter the item: ' + self.label_consumable) self.stdscr.addstr(12, 4, 'Enter the item: ' + self.label_consumable)
self.stdscr.clrtoeol()
for i in [8, 10, 14]:
self.stdscr.move(i, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Print [ESC] Cancel')
elif self.sub_screen == 'forum_search': elif self.sub_screen == 'forum_search':
self.stdscr.addstr(14, 4, 'Search for a thread: ' + self.label_forum_search) self.stdscr.addstr(14, 4, 'Search for a thread: ' + self.label_forum_search)
self.stdscr.clrtoeol()
for i in [8, 10, 12]:
self.stdscr.move(i, 4)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Search [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Search [ESC] Cancel')
elif self.sub_screen == 'forum_results': elif self.sub_screen == 'forum_results':
if self.search_results is not None: if self.search_results is not None:
@@ -608,70 +710,150 @@ class LabelScreen(Screen):
self.is_typing = self.sub_screen not in ['menu', 'forum_results'] self.is_typing = self.sub_screen not in ['menu', 'forum_results']
if self.sub_screen == 'menu': if self.sub_screen == 'menu':
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
elif button == 't': self.sub_screen = 'tool'; self.label_tool = '_' self.state.current_screen = 'home'
elif button == 's': self.sub_screen = 'material_name'; self.label_material_name = '_' elif button == 't':
elif button == 'g': self.sub_screen = 'generic'; self.label_generic = '_' self.sub_screen = 'tool'
elif button == 'c': self.sub_screen = 'consumable'; self.label_consumable = '_' self.label_tool = '_'
elif button == 'f': self.sub_screen = 'forum_search'; self.label_forum_search = '_' self.is_typing = True
else: self.try_highlight(c) elif button == 's':
self.sub_screen = 'material_name'
self.label_material_name = '_'
self.is_typing = True
elif button == 'g':
self.sub_screen = 'generic'
self.label_generic = '_'
self.is_typing = True
elif button == 'c':
self.sub_screen = 'consumable'
self.label_consumable = '_'
self.is_typing = True
elif button == 'f':
self.sub_screen = 'forum_search'
self.label_forum_search = '_'
self.is_typing = True
else:
self.try_highlight(c)
elif self.sub_screen == 'tool': elif self.sub_screen == 'tool':
if c == KEY_ESCAPE: self.on_enter() if c == KEY_ESCAPE:
self.on_enter()
self.stdscr.erase()
elif c == KEY_ENTER: elif c == KEY_ENTER:
if len(self.label_tool) > 1: if len(self.label_tool) > 1:
self.stdscr.addstr(15, 4, 'Printing...'); self.stdscr.refresh() self.stdscr.addstr(15, 4, 'Printing...')
try: utils.print_tool_label(self.label_tool[:-1]) self.stdscr.refresh()
except: self.stdscr.addstr(15, 4, 'Error.'); self.stdscr.refresh(); time.sleep(2) try:
utils.print_tool_label(self.label_tool[:-1])
except:
self.stdscr.addstr(15, 4, 'Error.')
self.stdscr.refresh()
time.sleep(2)
self.on_enter() self.on_enter()
self.stdscr.erase()
elif c <= 57 and c >= 48: elif c <= 57 and c >= 48:
self.label_tool = self.handle_text_input(c, self.label_tool) self.label_tool = self.handle_text_input(c, self.label_tool)
elif self.sub_screen == 'material_name': elif self.sub_screen == 'material_name':
if c == KEY_ESCAPE: self.on_enter() if c == KEY_ESCAPE:
self.on_enter()
self.stdscr.erase()
elif c == KEY_ENTER and len(self.label_material_name) > 1: elif c == KEY_ENTER and len(self.label_material_name) > 1:
self.sub_screen = 'material_contact'; self.label_material_contact = '_' self.sub_screen = 'material_contact'
else: self.label_material_name = self.handle_text_input(c, self.label_material_name) self.label_material_contact = '_'
else:
self.label_material_name = self.handle_text_input(c, self.label_material_name)
elif self.sub_screen == 'material_contact': elif self.sub_screen == 'material_contact':
if c == KEY_ESCAPE: self.on_enter() if c == KEY_ESCAPE:
self.on_enter()
self.stdscr.erase()
elif c == KEY_ENTER and len(self.label_material_contact) > 1: elif c == KEY_ENTER and len(self.label_material_contact) > 1:
self.stdscr.addstr(15, 4, 'Printing...'); self.stdscr.refresh() self.stdscr.addstr(15, 4, 'Printing...')
self.stdscr.refresh()
utils.print_sheet_label(self.label_material_name[:-1], self.label_material_contact[:-1]) utils.print_sheet_label(self.label_material_name[:-1], self.label_material_contact[:-1])
self.on_enter() self.on_enter()
else: self.label_material_contact = self.handle_text_input(c, self.label_material_contact) self.stdscr.erase()
else:
self.label_material_contact = self.handle_text_input(c, self.label_material_contact)
elif self.sub_screen == 'generic': elif self.sub_screen == 'generic':
if c == KEY_ESCAPE: self.on_enter() if c == KEY_ESCAPE:
self.on_enter()
self.stdscr.erase()
elif c == KEY_ENTER and len(self.label_generic) > 1: elif c == KEY_ENTER and len(self.label_generic) > 1:
self.stdscr.addstr(15, 4, 'Printing...'); self.stdscr.refresh() self.stdscr.addstr(15, 4, 'Printing...')
try: utils.print_generic_label(self.label_generic[:-1]) self.stdscr.refresh()
except: self.stdscr.addstr(15, 4, 'Error.'); self.stdscr.refresh(); time.sleep(2) try:
utils.print_generic_label(self.label_generic[:-1])
except:
self.stdscr.addstr(15, 4, 'Error.')
self.stdscr.refresh()
time.sleep(2)
self.on_enter() self.on_enter()
else: self.label_generic = self.handle_text_input(c, self.label_generic) self.stdscr.erase()
else:
self.label_generic = self.handle_text_input(c, self.label_generic)
elif self.sub_screen == 'consumable': elif self.sub_screen == 'consumable':
if c == KEY_ESCAPE: self.on_enter() if c == KEY_ESCAPE:
elif c == KEY_ENTER and len(self.label_consumable) > 1:
self.stdscr.addstr(15, 4, 'Printing...'); self.stdscr.refresh()
try: utils.print_consumable_label(self.label_consumable[:-1])
except: self.stdscr.addstr(15, 4, 'Error.'); self.stdscr.refresh(); time.sleep(2)
self.on_enter() self.on_enter()
else: self.label_consumable = self.handle_text_input(c, self.label_consumable) self.stdscr.erase()
elif c == KEY_ENTER and len(self.label_consumable) > 1:
self.stdscr.addstr(15, 4, 'Printing...')
self.stdscr.refresh()
try:
utils.print_consumable_label(self.label_consumable[:-1])
except:
self.stdscr.addstr(15, 4, 'Error.')
self.stdscr.refresh()
time.sleep(2)
self.on_enter()
self.stdscr.erase()
else:
self.label_consumable = self.handle_text_input(c, self.label_consumable)
elif self.sub_screen == 'forum_search': elif self.sub_screen == 'forum_search':
if c == KEY_ESCAPE: self.on_enter() if c == KEY_ESCAPE:
self.on_enter()
self.stdscr.erase()
elif c == KEY_ENTER and len(self.label_forum_search) > 2: elif c == KEY_ENTER and len(self.label_forum_search) > 2:
self.stdscr.addstr(16, 4, 'Searching...'); self.stdscr.refresh() self.stdscr.addstr(16, 4, 'Searching...')
try: self.search_results = utils.search_forum_thread(self.label_forum_search[:-1]) self.stdscr.refresh()
except: self.stdscr.addstr(16, 4, 'Error.'); self.stdscr.refresh(); time.sleep(2) try:
self.search_results = utils.search_forum_thread(self.label_forum_search[:-1])
except:
self.stdscr.addstr(16, 4, 'Error.')
self.stdscr.refresh()
time.sleep(2)
self.on_enter()
self.stdscr.erase()
return
self.sub_screen = 'forum_results' self.sub_screen = 'forum_results'
else: self.label_forum_search = self.handle_text_input(c, self.label_forum_search) self.stdscr.erase()
else:
self.label_forum_search = self.handle_text_input(c, self.label_forum_search)
elif self.sub_screen == 'forum_results': elif self.sub_screen == 'forum_results':
if c == KEY_ESCAPE: self.on_enter() if c == KEY_ESCAPE:
self.on_enter()
self.stdscr.erase()
elif c >= 49 and c <= 57: elif c >= 49 and c <= 57:
num = int(chr(c)) num = int(chr(c))
if self.search_results and num <= len(self.search_results): if self.search_results and num <= len(self.search_results):
self.stdscr.addstr(21, 1, 'Printing...'); self.stdscr.refresh() self.stdscr.addstr(21, 1, 'Printing...')
try: utils.print_forum_label(self.search_results[num-1]) self.stdscr.refresh()
except: self.stdscr.addstr(15, 4, 'Error.'); self.stdscr.refresh(); time.sleep(2) try:
utils.print_forum_label(self.search_results[num-1])
except:
self.stdscr.addstr(15, 4, 'Error.')
self.stdscr.refresh()
time.sleep(2)
self.on_enter() self.on_enter()
else: self.try_highlight(c) self.stdscr.erase()
else: self.try_highlight(c) else:
self.try_highlight(c)
else:
self.try_highlight(c)
def is_entry_key(self, c):
if self.sub_screen == 'menu':
button = self.get_button(c)
return button in ['t', 's', 'g', 'c', 'f']
return False
class GamesScreen(Screen): class GamesScreen(Screen):
@@ -692,17 +874,28 @@ class GamesScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
game_path = None game_path = None
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
elif button == 's' and utils.HAS_SUDOKU: game_path = utils.SUDOKU_LOCATION + ' -c' self.state.current_screen = 'home'
elif button == 'h' and utils.HAS_FROTZ and utils.HAS_HITCHHIKERS: game_path = utils.FROTZ_LOCATION + ' ' + utils.HITCHHIKERS_LOCATION elif button == 's' and utils.HAS_SUDOKU:
elif button == '2' and utils.HAS_2048: game_path = utils._2048_LOCATION game_path = utils.SUDOKU_LOCATION + ' -c'
elif button == 'm' and utils.HAS_MORIA: game_path = utils.MORIA_LOCATION elif button == 'h' and utils.HAS_FROTZ and utils.HAS_HITCHHIKERS:
elif button == 'n' and utils.HAS_NETHACK: game_path = utils.NETHACK_LOCATION game_path = utils.FROTZ_LOCATION + ' ' + utils.HITCHHIKERS_LOCATION
elif button == 'd' and utils.HAS_DRUGWARS: game_path = utils.DRUGWARS_LOCATION elif button == '2' and utils.HAS_2048:
else: self.try_highlight(c) game_path = utils._2048_LOCATION
elif button == 'm' and utils.HAS_MORIA:
game_path = utils.MORIA_LOCATION
elif button == 'n' and utils.HAS_NETHACK:
game_path = utils.NETHACK_LOCATION
elif button == 'd' and utils.HAS_DRUGWARS:
game_path = utils.DRUGWARS_LOCATION
else:
self.try_highlight(c)
if game_path: if game_path:
curses.nocbreak(); self.stdscr.keypad(False); curses.echo(); curses.endwin() curses.nocbreak()
self.stdscr.keypad(False)
curses.echo()
curses.endwin()
logging.info('Spawning game: %s', game_path) logging.info('Spawning game: %s', game_path)
os.system(game_path) os.system(game_path)
exit() exit()
@@ -740,33 +933,51 @@ I will be terse in my responses.''')]
self.stdscr.addstr(num + 5, 1, line) self.stdscr.addstr(num + 5, 1, line)
if self.is_typing: if self.is_typing:
self.stdscr.addstr(21, 21, self.message_to_send) self.stdscr.addstr(21, 21, self.message_to_send)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Send [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Send [ESC] Cancel')
else: else:
self.stdscr.addstr(21, 21, '[E] Edit message', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(21, 21, '[E] Edit message', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol() self.stdscr.clrtoeol()
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if self.is_typing: if self.is_typing:
if c == KEY_ESCAPE: self.is_typing = False; self.message_to_send = ''; self.stdscr.erase() if c == KEY_ESCAPE:
self.is_typing = False
self.message_to_send = ''
self.stdscr.erase()
elif c == KEY_ENTER: elif c == KEY_ENTER:
if len(self.message_to_send) > 1: if len(self.message_to_send) > 1:
self.stdscr.addstr(21, 21, 'Sending...'); self.stdscr.clrtoeol(); self.stdscr.refresh() self.stdscr.addstr(21, 21, 'Sending...')
self.stdscr.clrtoeol()
self.stdscr.refresh()
self.message_to_send = self.message_to_send[:-1] self.message_to_send = self.message_to_send[:-1]
lines = textwrap.wrap(self.message_to_send, width=80, initial_indent=' '*20, subsequent_indent=' '*20) lines = textwrap.wrap(self.message_to_send, width=80, initial_indent=' '*20, subsequent_indent=' '*20)
self.messages.append(''); self.messages.extend(lines) self.messages.append('')
self.messages.extend(lines)
self.thread.append(dict(role='user', content=self.message_to_send)) self.thread.append(dict(role='user', content=self.message_to_send))
gpt_reply = utils.message_protovac(self.thread) gpt_reply = utils.message_protovac(self.thread)
self.thread.append(gpt_reply) self.thread.append(gpt_reply)
lines = textwrap.wrap(gpt_reply['content'], width=60) lines = textwrap.wrap(gpt_reply['content'], width=60)
self.messages.append(''); self.messages.extend(lines) self.messages.append('')
self.messages.extend(lines)
self.stdscr.erase() self.stdscr.erase()
self.message_to_send = '_' self.message_to_send = '_'
else: self.message_to_send = self.handle_text_input(c, self.message_to_send) else:
elif button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' self.message_to_send = self.handle_text_input(c, self.message_to_send)
elif button == 'e': self.is_typing = True; self.message_to_send = '_' elif button == 'b' or c == KEY_ESCAPE:
else: self.try_highlight(c) self.state.current_screen = 'home'
elif button == 'e':
self.is_typing = True
self.message_to_send = '_'
else:
self.try_highlight(c)
def is_entry_key(self, c):
button = self.get_button(c)
return not self.is_typing and button == 'e'
class ThinkScreen(Screen): class ThinkScreen(Screen):
@@ -787,29 +998,54 @@ class ThinkScreen(Screen):
self.stdscr.addstr(5, 1, 'Give Protovac something to think about.') self.stdscr.addstr(5, 1, 'Give Protovac something to think about.')
if self.is_typing: if self.is_typing:
self.stdscr.addstr(7, 4, self.think_to_send) self.stdscr.addstr(7, 4, self.think_to_send)
self.stdscr.clrtoeol()
for i in range(9, 19):
self.stdscr.move(i, 1)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[RETURN] Send [ESC] Cancel') self.stdscr.addstr(23, 1, '[RETURN] Send [ESC] Cancel')
elif self.think_result: elif self.think_result:
self.stdscr.move(7, 4)
self.stdscr.clrtoeol()
for i in range(11, 19):
self.stdscr.move(i, 1)
self.stdscr.clrtoeol()
for _ in range(100): for _ in range(100):
try: self.stdscr.addstr(9, 4, self.think_result); break try:
except: self.think_result = self.think_result[:-5] self.stdscr.addstr(9, 4, self.think_result)
break
except:
self.think_result = self.think_result[:-5]
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0)
else: else:
self.stdscr.addstr(7, 4, '[E] Edit prompt', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(7, 4, '[E] Edit prompt', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.clrtoeol()
self.stdscr.move(9, 1)
self.stdscr.clrtoeol()
self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0)
self.stdscr.addstr(9, 1, 'Examples:') self.stdscr.addstr(9, 1, 'Examples:')
self.stdscr.addstr(11, 4, '42 + 69'); self.stdscr.addstr(12, 4, '55 kg to lbs') self.stdscr.addstr(11, 4, '42 + 69')
self.stdscr.addstr(13, 4, 'density of lead'); self.stdscr.addstr(14, 4, 'if x = 4, what is 3x + 50?') self.stdscr.addstr(12, 4, '55 kg to lbs')
self.stdscr.addstr(15, 4, 'force m=150g, a=50cm/s^2'); self.stdscr.addstr(16, 4, 'boiling point of benzene at 550 torr') self.stdscr.addstr(13, 4, 'density of lead')
self.stdscr.addstr(17, 4, 'goats with highest milk yield'); self.stdscr.addstr(18, 4, 'how long did the Aztec empire last?') self.stdscr.addstr(14, 4, 'if x = 4, what is 3x + 50?')
self.stdscr.addstr(15, 4, 'force m=150g, a=50cm/s^2')
self.stdscr.addstr(16, 4, 'boiling point of benzene at 550 torr')
self.stdscr.addstr(17, 4, 'goats with highest milk yield')
self.stdscr.addstr(18, 4, 'how long did the Aztec empire last?')
self.stdscr.clrtoeol() self.stdscr.clrtoeol()
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if self.is_typing: if self.is_typing:
if c == KEY_ESCAPE: self.is_typing = False; self.think_to_send = ''; self.stdscr.erase() if c == KEY_ESCAPE:
self.is_typing = False
self.think_to_send = ''
self.stdscr.erase()
elif c == KEY_ENTER: elif c == KEY_ENTER:
if len(self.think_to_send) > 1: if len(self.think_to_send) > 1:
self.stdscr.addstr(9, 4, 'Thinking...'); self.stdscr.clrtoeol(); self.stdscr.refresh() self.stdscr.addstr(9, 4, 'Thinking...')
self.stdscr.clrtoeol()
self.stdscr.refresh()
query = self.think_to_send[:-1] query = self.think_to_send[:-1]
logging.info('Thinking about: %s', query) logging.info('Thinking about: %s', query)
self.think_result = utils.think_send(query) self.think_result = utils.think_send(query)
@@ -817,30 +1053,44 @@ class ThinkScreen(Screen):
self.is_typing = False self.is_typing = False
self.think_to_send = '' self.think_to_send = ''
self.stdscr.erase() self.stdscr.erase()
else: self.think_to_send = self.handle_text_input(c, self.think_to_send) else:
elif button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' self.think_to_send = self.handle_text_input(c, self.think_to_send)
elif button == 'e': self.is_typing = True; self.think_to_send = '_' elif button == 'b' or c == KEY_ESCAPE:
else: self.try_highlight(c) self.state.current_screen = 'home'
elif button == 'e':
self.is_typing = True
self.think_to_send = '_'
else:
self.try_highlight(c)
def is_entry_key(self, c):
button = self.get_button(c)
return not self.is_typing and button == 'e'
class AboutScreen(Screen): class AboutScreen(Screen):
def draw(self): def draw(self):
self.stdscr.addstr(0, 1, 'PROTOVAC UNIVERSAL COMPUTER') self.stdscr.addstr(0, 1, 'PROTOVAC UNIVERSAL COMPUTER')
self.stdscr.addstr(2, 1, 'About'); self.stdscr.addstr(3, 1, '=====') self.stdscr.addstr(2, 1, 'About')
self.stdscr.addstr(3, 1, '=====')
self.stdscr.addstr(5, 1, 'Protovac is a universal mainframe computer accessible by terminal.') self.stdscr.addstr(5, 1, 'Protovac is a universal mainframe computer accessible by terminal.')
self.stdscr.addstr(7, 1, 'License'); self.stdscr.addstr(8, 1, '-------') self.stdscr.addstr(7, 1, 'License')
self.stdscr.addstr(8, 1, '-------')
self.stdscr.addstr(10, 1, 'This program is free and open-source software licensed under the MIT License.') self.stdscr.addstr(10, 1, 'This program is free and open-source software licensed under the MIT License.')
self.stdscr.addstr(11, 1, 'Please see the LICENSE file for details. This means you have the right to') self.stdscr.addstr(11, 1, 'Please see the LICENSE file for details. This means you have the right to')
self.stdscr.addstr(12, 1, 'study, change, and distribute the software and source code to anyone and for') self.stdscr.addstr(12, 1, 'study, change, and distribute the software and source code to anyone and for')
self.stdscr.addstr(13, 1, 'any purpose.') self.stdscr.addstr(13, 1, 'any purpose.')
self.stdscr.addstr(15, 1, 'Source code: github.com/Protospace/protovac') self.stdscr.addstr(15, 1, 'Source code: github.com/Protospace/protovac')
self.stdscr.addstr(17, 1, 'Acknowledgements'); self.stdscr.addstr(18, 1, '----------------') self.stdscr.addstr(17, 1, 'Acknowledgements')
self.stdscr.addstr(18, 1, '----------------')
self.stdscr.addstr(20, 1, 'Thanks to Peter for lending the Morrow MTD-60 terminal and Jamie for the Pi.') self.stdscr.addstr(20, 1, 'Thanks to Peter for lending the Morrow MTD-60 terminal and Jamie for the Pi.')
self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0) self.stdscr.addstr(23, 1, '[B] Back', curses.A_REVERSE if self.state.highlight_keys else 0)
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 'b' or c == KEY_ESCAPE: self.state.current_screen = 'home' if button == 'b' or c == KEY_ESCAPE:
else: self.try_highlight(c) self.state.current_screen = 'home'
else:
self.try_highlight(c)
class HelpScreen(Screen): class HelpScreen(Screen):
def draw(self): def draw(self):
@@ -852,5 +1102,7 @@ class HelpScreen(Screen):
def handle_input(self, c): def handle_input(self, c):
button = self.get_button(c) button = self.get_button(c)
if button == 'o': self.state.current_screen = 'home' if button == 'o':
else: self.try_highlight(c) self.state.current_screen = 'home'
else:
self.try_highlight(c)