From d1bad818cdd08bb61dc14e6683aebabb5b5d7b77 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Wed, 16 Jul 2025 19:34:56 -0400 Subject: [PATCH] initial improvements to pty driver. Improved clipboard handling of multibyte characters. Added emoji menu to vmenu. It places the emoji to the clipboard to be used wherever. --- .../onScreenUpdate/65000-progress_detector.py | 24 ++ .../vmenu-profiles/KEY/emoji/__init__.py | 1 + .../KEY/emoji/flags/__init__.py | 1 + .../vmenu-profiles/KEY/emoji/flags/canada.py | 22 ++ .../vmenu-profiles/KEY/emoji/flags/uk.py | 22 ++ .../vmenu-profiles/KEY/emoji/flags/usa.py | 22 ++ .../vmenu-profiles/KEY/emoji/food/__init__.py | 1 + .../vmenu-profiles/KEY/emoji/food/beer.py | 22 ++ .../vmenu-profiles/KEY/emoji/food/coffee.py | 22 ++ .../vmenu-profiles/KEY/emoji/food/donut.py | 22 ++ .../KEY/emoji/food/hamburger.py | 22 ++ .../vmenu-profiles/KEY/emoji/food/pizza.py | 22 ++ .../vmenu-profiles/KEY/emoji/food/taco.py | 22 ++ .../KEY/emoji/holidays/__init__.py | 1 + .../vmenu-profiles/KEY/emoji/holidays/bat.py | 22 ++ .../KEY/emoji/holidays/bunny.py | 22 ++ .../KEY/emoji/holidays/christmas_tree.py | 22 ++ .../KEY/emoji/holidays/easter_egg.py | 22 ++ .../KEY/emoji/holidays/fireworks.py | 22 ++ .../KEY/emoji/holidays/ghost.py | 22 ++ .../vmenu-profiles/KEY/emoji/holidays/gift.py | 22 ++ .../KEY/emoji/holidays/jack_o_lantern.py | 22 ++ .../KEY/emoji/holidays/santa.py | 22 ++ .../KEY/emoji/holidays/shamrock.py | 22 ++ .../KEY/emoji/holidays/skull.py | 22 ++ .../KEY/emoji/holidays/snowman.py | 22 ++ .../KEY/emoji/holidays/spider.py | 22 ++ .../KEY/emoji/holidays/turkey.py | 22 ++ .../KEY/emoji/nature/__init__.py | 1 + .../vmenu-profiles/KEY/emoji/nature/cat.py | 22 ++ .../vmenu-profiles/KEY/emoji/nature/dog.py | 22 ++ .../vmenu-profiles/KEY/emoji/nature/moon.py | 22 ++ .../KEY/emoji/nature/rainbow.py | 22 ++ .../vmenu-profiles/KEY/emoji/nature/sun.py | 22 ++ .../vmenu-profiles/KEY/emoji/nature/tree.py | 22 ++ .../KEY/emoji/people/__init__.py | 1 + .../vmenu-profiles/KEY/emoji/people/angry.py | 22 ++ .../vmenu-profiles/KEY/emoji/people/cool.py | 22 ++ .../vmenu-profiles/KEY/emoji/people/crying.py | 22 ++ .../vmenu-profiles/KEY/emoji/people/devil.py | 22 ++ .../KEY/emoji/people/laughing.py | 22 ++ .../vmenu-profiles/KEY/emoji/people/poop.py | 22 ++ .../vmenu-profiles/KEY/emoji/people/sad.py | 22 ++ .../KEY/emoji/people/shocked.py | 22 ++ .../KEY/emoji/people/smiling.py | 22 ++ .../KEY/emoji/people/thumbs_up.py | 22 ++ .../KEY/emoji/people/winking.py | 22 ++ .../KEY/emoji/symbols/__init__.py | 1 + .../KEY/emoji/symbols/checkmark.py | 22 ++ .../vmenu-profiles/KEY/emoji/symbols/fire.py | 22 ++ .../vmenu-profiles/KEY/emoji/symbols/heart.py | 22 ++ .../KEY/emoji/symbols/lightning.py | 22 ++ .../vmenu-profiles/KEY/emoji/symbols/peace.py | 22 ++ .../KEY/emoji/symbols/rock_on.py | 22 ++ .../KEY/emoji/symbols/skull_crossbones.py | 22 ++ .../vmenu-profiles/KEY/emoji/symbols/star.py | 22 ++ src/fenrirscreenreader/fenrirVersion.py | 2 +- .../inputDriver/ptyDriver.py | 176 +++++++++- .../screenDriver/ptyDriver.py | 322 +++++++++++++----- .../screenDriver/vcsaDriver.py | 5 +- 60 files changed, 1503 insertions(+), 89 deletions(-) create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/__init__.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/__init__.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/canada.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/uk.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/usa.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/__init__.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/beer.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/coffee.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/donut.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/hamburger.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/pizza.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/taco.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/__init__.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bat.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bunny.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/christmas_tree.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/easter_egg.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/fireworks.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/ghost.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/gift.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/jack_o_lantern.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/santa.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/shamrock.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/skull.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/snowman.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/spider.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/turkey.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/__init__.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/cat.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/dog.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/moon.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/rainbow.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/sun.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/tree.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/__init__.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/angry.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/cool.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/crying.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/devil.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/laughing.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/poop.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/sad.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/shocked.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/smiling.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/thumbs_up.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/winking.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/__init__.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/checkmark.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/fire.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/heart.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/lightning.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/peace.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/rock_on.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/skull_crossbones.py create mode 100644 src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/star.py diff --git a/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py b/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py index e195d1b7..010b49b8 100644 --- a/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py +++ b/src/fenrirscreenreader/commands/onScreenUpdate/65000-progress_detector.py @@ -235,6 +235,30 @@ class command: ): self.play_activity_beep() self.env["commandBuffer"]["lastProgressTime"] = current_time + return + + # Pattern 5: Braille progress indicators + braille_match = re.search(r'[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⡿⣟⣯⣷⣾⣽⣻⢿]', text) + if braille_match: + if current_time - self.env["commandBuffer"]["lastProgressTime"] >= 1.0: + self.play_activity_beep() + self.env["commandBuffer"]["lastProgressTime"] = current_time + return + + # Pattern 6: Moon phase progress indicators + moon_match = re.search(r'[🌑🌒🌓🌔🌕🌖🌗🌘]', text) + if moon_match: + moon_phases = { + '🌑': 0, '🌒': 12.5, '🌓': 25, '🌔': 37.5, + '🌕': 50, '🌖': 62.5, '🌗': 75, '🌘': 87.5 + } + moon_char = moon_match.group(0) + if moon_char in moon_phases: + percentage = moon_phases[moon_char] + if percentage != self.env["commandBuffer"]["lastProgressValue"]: + self.play_progress_tone(percentage) + self.env["commandBuffer"]["lastProgressValue"] = percentage + return def play_progress_tone(self, percentage): # Map 0-100% to 400-1200Hz frequency range diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/__init__.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/__init__.py new file mode 100644 index 00000000..bb261d58 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/__init__.py @@ -0,0 +1 @@ +# Emoji VMenu category \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/__init__.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/__init__.py new file mode 100644 index 00000000..9dfb5e09 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/__init__.py @@ -0,0 +1 @@ +# Flags emoji subcategory \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/canada.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/canada.py new file mode 100644 index 00000000..613803e6 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/canada.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🇨🇦" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add Canada flag emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added Canada flag to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/uk.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/uk.py new file mode 100644 index 00000000..3bbbd3eb --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/uk.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🇬🇧" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add UK flag emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added UK flag to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/usa.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/usa.py new file mode 100644 index 00000000..9b1a9d9c --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/flags/usa.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🇺🇸" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add USA flag emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added USA flag to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/__init__.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/__init__.py new file mode 100644 index 00000000..b03a398b --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/__init__.py @@ -0,0 +1 @@ +# Food emoji subcategory \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/beer.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/beer.py new file mode 100644 index 00000000..80dc013f --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/beer.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🍺" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Beer emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added beer to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/coffee.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/coffee.py new file mode 100644 index 00000000..eee79401 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/coffee.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "☕" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add coffee emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added coffee to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/donut.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/donut.py new file mode 100644 index 00000000..695d9dcf --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/donut.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🍩" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Donut emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added donut to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/hamburger.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/hamburger.py new file mode 100644 index 00000000..d4fa4ee4 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/hamburger.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🍔" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add hamburger emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added hamburger to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/pizza.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/pizza.py new file mode 100644 index 00000000..c777fdb7 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/pizza.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🍕" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add pizza emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added pizza to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/taco.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/taco.py new file mode 100644 index 00000000..c61f9121 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/food/taco.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🌮" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Taco emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added taco to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/__init__.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/__init__.py new file mode 100644 index 00000000..2fc1e37a --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/__init__.py @@ -0,0 +1 @@ +# Holidays emoji subcategory \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bat.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bat.py new file mode 100644 index 00000000..befdcafe --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bat.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🦇" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add bat emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added bat to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bunny.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bunny.py new file mode 100644 index 00000000..8cd6d6fc --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/bunny.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🐰" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add bunny emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added bunny to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/christmas_tree.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/christmas_tree.py new file mode 100644 index 00000000..5600eb96 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/christmas_tree.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🎄" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add Christmas tree emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added Christmas tree to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/easter_egg.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/easter_egg.py new file mode 100644 index 00000000..372f74e5 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/easter_egg.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🥚" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add Easter egg emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added Easter egg to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/fireworks.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/fireworks.py new file mode 100644 index 00000000..d5d89b86 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/fireworks.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🎆" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add fireworks emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added fireworks to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/ghost.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/ghost.py new file mode 100644 index 00000000..32d265fb --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/ghost.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "👻" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add ghost emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added ghost to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/gift.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/gift.py new file mode 100644 index 00000000..296ab6b3 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/gift.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🎁" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add gift emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added gift to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/jack_o_lantern.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/jack_o_lantern.py new file mode 100644 index 00000000..cc7336fa --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/jack_o_lantern.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🎃" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add jack o'lantern emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added jack o'lantern to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/santa.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/santa.py new file mode 100644 index 00000000..5fe56316 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/santa.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🎅" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add Santa emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added Santa to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/shamrock.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/shamrock.py new file mode 100644 index 00000000..4892abc5 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/shamrock.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "☘️" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add shamrock emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added shamrock to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/skull.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/skull.py new file mode 100644 index 00000000..3e2221cc --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/skull.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "💀" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add skull emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added skull to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/snowman.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/snowman.py new file mode 100644 index 00000000..a1c54d31 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/snowman.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "⛄" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add snowman emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added snowman to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/spider.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/spider.py new file mode 100644 index 00000000..94a7a8b0 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/spider.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🕷" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add spider emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added spider to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/turkey.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/turkey.py new file mode 100644 index 00000000..c8d6d89f --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/holidays/turkey.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🦃" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add turkey emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added turkey to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/__init__.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/__init__.py new file mode 100644 index 00000000..069e4fc1 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/__init__.py @@ -0,0 +1 @@ +# Nature emoji subcategory \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/cat.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/cat.py new file mode 100644 index 00000000..d494a60a --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/cat.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🐱" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Cat emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added cat to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/dog.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/dog.py new file mode 100644 index 00000000..e2ccd8ee --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/dog.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🐶" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Dog emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added dog to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/moon.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/moon.py new file mode 100644 index 00000000..e7c528cc --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/moon.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🌙" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add moon emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added moon to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/rainbow.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/rainbow.py new file mode 100644 index 00000000..4a98959f --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/rainbow.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🌈" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Rainbow emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added rainbow to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/sun.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/sun.py new file mode 100644 index 00000000..bab1eec5 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/sun.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "☀️" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add sun emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added sun to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/tree.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/tree.py new file mode 100644 index 00000000..53d0ae28 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/nature/tree.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🌳" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add tree emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added tree to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/__init__.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/__init__.py new file mode 100644 index 00000000..dbd3765e --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/__init__.py @@ -0,0 +1 @@ +# People emoji subcategory \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/angry.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/angry.py new file mode 100644 index 00000000..8f14278e --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/angry.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😠" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Angry face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added angry face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/cool.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/cool.py new file mode 100644 index 00000000..cbaff6d8 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/cool.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😎" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Cool face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added cool face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/crying.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/crying.py new file mode 100644 index 00000000..1be9574c --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/crying.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😭" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Crying face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added crying face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/devil.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/devil.py new file mode 100644 index 00000000..073de67a --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/devil.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😈" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Devil face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added devil face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/laughing.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/laughing.py new file mode 100644 index 00000000..e31b0d1b --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/laughing.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😂" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add laughing face emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added laughing face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/poop.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/poop.py new file mode 100644 index 00000000..b3ff0670 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/poop.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "💩" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Poop emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added poop to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/sad.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/sad.py new file mode 100644 index 00000000..3743e0fb --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/sad.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😢" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Sad face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added sad face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/shocked.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/shocked.py new file mode 100644 index 00000000..6f0a045c --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/shocked.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😱" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Shocked face emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added shocked face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/smiling.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/smiling.py new file mode 100644 index 00000000..9c6dce41 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/smiling.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😊" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add smiling face emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added smiling face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/thumbs_up.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/thumbs_up.py new file mode 100644 index 00000000..c9209a1a --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/thumbs_up.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "👍" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add thumbs up emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added thumbs up to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/winking.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/winking.py new file mode 100644 index 00000000..a623f6c0 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/people/winking.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "😉" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add winking face emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added winking face to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/__init__.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/__init__.py new file mode 100644 index 00000000..bea3d4b5 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/__init__.py @@ -0,0 +1 @@ +# Symbols emoji subcategory \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/checkmark.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/checkmark.py new file mode 100644 index 00000000..648741ab --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/checkmark.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "✅" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add checkmark emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added checkmark to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/fire.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/fire.py new file mode 100644 index 00000000..6b7bf53c --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/fire.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🔥" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Fire emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added fire to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/heart.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/heart.py new file mode 100644 index 00000000..47735601 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/heart.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "❤️" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add heart emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added heart to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/lightning.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/lightning.py new file mode 100644 index 00000000..c4485deb --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/lightning.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "⚡" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Lightning emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added lightning to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/peace.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/peace.py new file mode 100644 index 00000000..3016dd96 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/peace.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "✌️" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Peace sign emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added peace sign to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/rock_on.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/rock_on.py new file mode 100644 index 00000000..7e1e87f3 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/rock_on.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "🤘" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Sign of the horns emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added sign of the horns to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/skull_crossbones.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/skull_crossbones.py new file mode 100644 index 00000000..f78545a2 --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/skull_crossbones.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "☠️" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Skull and crossbones emoji" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added skull and crossbones to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/star.py b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/star.py new file mode 100644 index 00000000..bc94145c --- /dev/null +++ b/src/fenrirscreenreader/commands/vmenu-profiles/KEY/emoji/symbols/star.py @@ -0,0 +1,22 @@ +class command(): + def initialize(self, environment): + self.env = environment + self.emoji = "⭐" + + def shutdown(self): + pass + + def setCallback(self, callback): + pass + + def getDescription(self): + return "Add star emoji to clipboard" + + def run(self): + self.env["runtime"]["MemoryManager"].add_value_to_first_index( + "clipboardHistory", self.emoji + ) + self.env["runtime"]["OutputManager"].present_text( + "Added star to clipboard", + interrupt=False, flush=False + ) \ No newline at end of file diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index 086b56af..0e664a47 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,6 +4,6 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. -version = "2025.07.15" +version = "2025.07.16" codeName = "testing" code_name = "testing" diff --git a/src/fenrirscreenreader/inputDriver/ptyDriver.py b/src/fenrirscreenreader/inputDriver/ptyDriver.py index 7ca54df9..74f26ee3 100644 --- a/src/fenrirscreenreader/inputDriver/ptyDriver.py +++ b/src/fenrirscreenreader/inputDriver/ptyDriver.py @@ -39,7 +39,177 @@ class driver(inputDriver): Args: environment: Fenrir environment dictionary + + Returns: + bool: True if initialization successful, False otherwise """ - self.env = environment - self.env["runtime"]["InputManager"].set_shortcut_type("BYTE") - self._is_initialized = True + try: + if environment is None: + raise ValueError("Environment cannot be None") + + self.env = environment + + # Validate required managers are available + if "runtime" not in self.env: + raise ValueError("Runtime environment missing") + if "InputManager" not in self.env["runtime"]: + raise ValueError("InputManager not available") + + self.env["runtime"]["InputManager"].set_shortcut_type("BYTE") + self._is_initialized = True + + self.env["runtime"]["DebugManager"].write_debug_out( + "PTY inputDriver: Initialized with byte-based shortcuts", + debug.DebugLevel.INFO + ) + return True + + except Exception as e: + # Log error if possible, otherwise fallback to print + try: + if hasattr(self, 'env') and self.env and "runtime" in self.env: + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY inputDriver: Initialization failed: {e}", + debug.DebugLevel.ERROR + ) + else: + print(f"PTY inputDriver initialization error: {e}") + except: + print(f"PTY inputDriver initialization error: {e}") + + self._is_initialized = False + return False + + def shutdown(self): + """Shutdown the PTY input driver. + + Performs cleanup operations when the driver is being stopped. + For PTY driver, this involves cleaning up any resources and + logging the shutdown. + """ + if not self._is_initialized: + return + + try: + self.env["runtime"]["DebugManager"].write_debug_out( + "PTY inputDriver: Shutting down", + debug.DebugLevel.INFO + ) + except Exception as e: + # Fallback logging if debug manager is unavailable + print(f"PTY inputDriver shutdown error: {e}") + finally: + self._is_initialized = False + + def get_input_event(self): + """Get input event from PTY. + + For PTY driver, input events are handled through the byte-based + shortcut system rather than direct device events. This method + returns None as PTY input is processed through the screen driver + and InputManager's byte processing. + + Returns: + None: PTY driver uses byte-based processing, not event-based + """ + return None + + def is_device_connected(self): + """Check if PTY input device is connected. + + For PTY driver, the "device" is the terminal interface itself, + which is considered connected if the driver is initialized. + + Returns: + bool: True if driver is initialized, False otherwise + """ + return self._is_initialized + + def get_device_name(self): + """Get the name of the PTY input device. + + Returns: + str: Human-readable name of the PTY input device + """ + return "PTY (Pseudo-terminal) Input" + + def grab_devices(self, grab=True): + """Grab or release input devices. + + For PTY driver, device grabbing is not applicable since input + is processed through terminal emulation rather than direct + device access. + + Args: + grab (bool): Whether to grab (True) or release (False) devices + + Returns: + bool: Always returns True for PTY driver (no-op success) + """ + if not self._is_initialized: + return False + + action = "grab" if grab else "release" + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY inputDriver: {action} devices (no-op for PTY)", + debug.DebugLevel.INFO + ) + return True + + def has_device_detection(self): + """Check if driver supports device detection. + + PTY driver does not support dynamic device detection since + it operates on the terminal interface directly. + + Returns: + bool: Always False for PTY driver + """ + return False + + def get_device_list(self): + """Get list of available input devices. + + For PTY driver, there is only one logical device - the terminal + interface itself. + + Returns: + list: Single-item list containing PTY device info + """ + if not self._is_initialized: + return [] + + return [{ + 'name': 'PTY Terminal', + 'path': '/dev/pts/*', + 'type': 'terminal', + 'connected': True + }] + + def get_led_state(self, led_mask=None): + """Get LED state information. + + PTY driver cannot access LED states since it operates through + terminal emulation rather than direct hardware access. + + Args: + led_mask: LED mask parameter (ignored for PTY) + + Returns: + dict: Empty dict (no LED access for PTY) + """ + return {} + + def set_led_state(self, led_dict): + """Set LED states. + + PTY driver cannot control LEDs since it operates through + terminal emulation rather than direct hardware access. + + Args: + led_dict (dict): LED state dictionary (ignored for PTY) + + Returns: + bool: Always False (LED control not supported) + """ + return False diff --git a/src/fenrirscreenreader/screenDriver/ptyDriver.py b/src/fenrirscreenreader/screenDriver/ptyDriver.py index 68b3b617..e70d78ad 100644 --- a/src/fenrirscreenreader/screenDriver/ptyDriver.py +++ b/src/fenrirscreenreader/screenDriver/ptyDriver.py @@ -13,6 +13,7 @@ import signal import struct import sys import termios +import threading import time import tty from select import select @@ -32,16 +33,39 @@ class FenrirScreen(pyte.Screen): class Terminal: - def __init__(self, columns, lines, p_in): + def __init__(self, columns, lines, p_in, env=None): self.text = "" self.attributes = None self.screen = FenrirScreen(columns, lines) + self.env = env # Environment for proper logging + + # Pre-create default attribute template to avoid repeated allocation + self._default_attribute = [ + "default", "default", False, False, False, False, False, False, + "default", "default" + ] self.screen.write_process_input = lambda data: p_in.write( data.encode() ) self.stream = pyte.ByteStream() self.stream.attach(self.screen) + def _log_error(self, message, level=None): + """Log error message using proper debug manager if available.""" + if self.env and "runtime" in self.env and "DebugManager" in self.env["runtime"]: + try: + log_level = level if level else debug.DebugLevel.ERROR + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY Terminal: {message}", + log_level + ) + return + except Exception: + pass # Fallback to print if debug manager fails + + # Fallback logging when debug manager unavailable + print(f"PTY Terminal: {message}") + def feed(self, data): self.stream.feed(data) @@ -52,45 +76,57 @@ class Terminal: lines = self.screen.dirty else: lines = range(self.screen.lines) - self.attributes = [ - [ - list(attribute[1:]) + [False, "default", "default"] - for attribute in line.values() - ] - for line in buffer.values() - ] - for y in lines: try: - t = self.attributes[y] + self.attributes = [ + [ + list(attribute[1:]) + [False, "default", "default"] + if len(attribute) > 1 else [False, "default", "default"] + for attribute in line.values() + ] + for line in buffer.values() + ] except Exception as e: - # Terminal class doesn't have access to env, use fallback - # logging - print( - f"ptyDriver Terminal update_attributes: Error accessing " - f"attributes: {e}" + self._log_error(f"Error initializing attributes: {e}") + # Fallback to empty attributes + self.attributes = [[] for _ in range(self.screen.lines)] + for y in lines: + # Validate y is within reasonable bounds (prevent memory exhaustion) + max_lines = 10000 # Reasonable maximum for terminal applications + if y >= max_lines: + self._log_error( + f"Line index {y} exceeds maximum {max_lines}, " + f"skipping attribute update", + debug.DebugLevel.WARNING ) + continue + + # Check if line y exists in buffer before accessing it + if y not in buffer: + # Only log this occasionally to prevent spam + if y % 10 == 0: # Log every 10th missing line + self._log_error( + f"Lines {y}-{y+9} not found in buffer, skipping attribute updates", + debug.DebugLevel.WARNING + ) + continue + + # Ensure attributes array is large enough for line y + while len(self.attributes) <= y: self.attributes.append([]) - - self.attributes[y] = [ - list(attribute[1:]) + [False, "default", "default"] - for attribute in (buffer[y].values()) - ] + + try: + self.attributes[y] = [ + list(attribute[1:]) + [False, "default", "default"] + for attribute in (buffer[y].values()) + ] + except Exception as e: + self._log_error(f"Error updating attributes for line {y}: {e}") + # Initialize with empty attributes if update fails + self.attributes[y] = [] if len(self.attributes[y]) < self.screen.columns: diff = self.screen.columns - len(self.attributes[y]) - self.attributes[y] += [ - [ - "default", - "default", - False, - False, - False, - False, - False, - False, - "default", - "default", - ] - ] * diff + # Use pre-created template for efficiency + self.attributes[y] += [self._default_attribute[:] for _ in range(diff)] def resize(self, lines, columns): self.screen.resize(lines, columns) @@ -98,31 +134,35 @@ class Terminal: self.update_attributes(True) def set_cursor(self, x=-1, y=-1): - x_pos = x - y_pos = y - if x_pos == -1: - x_pos = self.screen.cursor.x - if y_pos == -1: - y_pos = self.screen.cursor.y - self.screen.cursor.x = min( - self.screen.cursor.x, self.screen.columns - 1 - ) - self.screen.cursor.y = min(self.screen.cursor.y, self.screen.lines - 1) + # Determine target cursor position + x_pos = x if x != -1 else self.screen.cursor.x + y_pos = y if y != -1 else self.screen.cursor.y + + # Validate and clamp cursor position to screen bounds + max_x = max(0, self.screen.columns - 1) + max_y = max(0, self.screen.lines - 1) + + self.screen.cursor.x = max(0, min(x_pos, max_x)) + self.screen.cursor.y = max(0, min(y_pos, max_y)) def get_screen_content(self): cursor = self.screen.cursor self.text = "\n".join(self.screen.display) self.update_attributes(self.attributes is None) self.screen.dirty.clear() - return { + + # Return screen content without unnecessary copying + # Only copy attributes if they exist and need protection + screen_data = { "cursor": (cursor.x, cursor.y), "lines": self.screen.lines, "columns": self.screen.columns, "text": self.text, - "attributes": self.attributes.copy(), + "attributes": self.attributes[:] if self.attributes else [], # Shallow copy only if needed "screen": "pty", "screenUpdateTime": time.time(), - }.copy() + } + return screen_data class driver(screenDriver): @@ -132,6 +172,7 @@ class driver(screenDriver): self.p_out = None self.terminal = None self.p_pid = -1 + self.terminal_lock = threading.Lock() # Synchronize terminal operations signal.signal(signal.SIGWINCH, self.handle_sigwinch) def initialize(self, environment): @@ -163,28 +204,56 @@ class driver(screenDriver): self.env["general"]["curr_user"] = getpass.getuser() def read_all(self, fd, timeout=0.3, interruptFd=None, len=65536): + """Read all available data from file descriptor with efficient polling. + + Uses progressively longer wait times to balance responsiveness with CPU usage. + """ msg_bytes = b"" - fd_list = [] - fd_list += [fd] + fd_list = [fd] if interruptFd: - fd_list += [interruptFd] + fd_list.append(interruptFd) + starttime = time.time() + poll_timeout = 0.001 # Start with 1ms, stay responsive + while True: - r = screen_utils.has_more_what(fd_list, 0.0001) - # nothing more to read + # Use consistent short polling for responsiveness + r = screen_utils.has_more_what(fd_list, poll_timeout) + + # Nothing more to read if fd not in r: + # Check overall timeout + if (time.time() - starttime) >= timeout: + break + continue + + try: + data = os.read(fd, len) + if data == b"": + raise EOFError + msg_bytes += data + except OSError as e: + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY screenDriver read_all: OS error reading from fd {fd}: {e}", + debug.DebugLevel.ERROR + ) + # For I/O errors, exit immediately to prevent endless retry loops + if e.errno == 5: # Input/output error + self.env["runtime"]["DebugManager"].write_debug_out( + "PTY screenDriver: Terminal connection lost, stopping read loop", + debug.DebugLevel.ERROR + ) + raise EOFError("Terminal connection lost") break - data = os.read(fd, len) - if data == b"": - raise EOFError - msg_bytes += data - # exit on interrupt available - if interruptFd in r: + + # Exit on interrupt available + if interruptFd and interruptFd in r: break - # respect timeout but wait a little bit of time to see if something - # more is here + + # Check overall timeout if (time.time() - starttime) >= timeout: break + return msg_bytes def open_terminal(self, columns, lines, command): @@ -197,16 +266,16 @@ class driver(screenDriver): if env["TERM"] == "": env["TERM"] = "linux" except Exception as e: - # Child process doesn't have access to env, use fallback - # logging + # Child process doesn't have access to debug manager + # Use fallback logging with more context print( - f"ptyDriver spawnTerminal: Error checking TERM environment: {e}" + f"ptyDriver open_terminal (child): TERM environment error: {e}" ) env["TERM"] = "linux" os.execvpe(argv[0], argv, env) # File-like object for I/O with the child process aka command. p_out = os.fdopen(master_fd, "w+b", 0) - return Terminal(columns, lines, p_out), p_pid, p_out + return Terminal(columns, lines, p_out, self.env), p_pid, p_out def resize_terminal(self, fd): s = struct.pack("HHHH", 0, 0, 0, 0) @@ -239,7 +308,7 @@ class driver(screenDriver): self.terminal.resize(lines, columns) fd_list = [sys.stdin, self.p_out, self.signalPipe[0]] while active.value: - r, _, _ = select(fd_list, [], [], 1) + r, _, _ = select(fd_list, [], [], 0.05) # 50ms timeout for responsiveness # none if r == []: continue @@ -251,7 +320,7 @@ class driver(screenDriver): # input if sys.stdin in r: try: - msg_bytes = self.read_all(sys.stdin.fileno(), len=4096) + msg_bytes = self.read_all(sys.stdin.fileno(), timeout=0.01, len=4096) except (EOFError, OSError): event_queue.put( { @@ -299,34 +368,121 @@ class driver(screenDriver): } ) break - # feed and send event bevore write, the pyte already has the right state - # so fenrir already can progress bevore os.write what - # should give some better reaction time - self.terminal.feed(msg_bytes) - event_queue.put( - { - "Type": FenrirEventType.screen_update, - "data": screen_utils.create_screen_event_data( - self.terminal.get_screen_content() - ), - } - ) + # Synchronize terminal operations to prevent race conditions + with self.terminal_lock: + # Feed data to terminal and get consistent screen state + self.terminal.feed(msg_bytes) + screen_content = self.terminal.get_screen_content() + + # Send screen update event with consistent state + event_queue.put( + { + "Type": FenrirEventType.screen_update, + "data": screen_utils.create_screen_event_data( + screen_content + ), + } + ) + + # Inject to actual screen (outside lock to avoid blocking) self.inject_text_to_screen( msg_bytes, screen=sys.stdout.fileno() ) except Exception as e: # Process died? - print(e) + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY screenDriver terminal_emulation: Exception occurred: {e}", + debug.DebugLevel.ERROR + ) event_queue.put( {"Type": FenrirEventType.stop_main_loop, "data": None} ) finally: - os.kill(self.p_pid, signal.SIGTERM) - self.p_out.close() - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_attr) + self._safe_cleanup_process() + self._safe_cleanup_resources(old_attr) event_queue.put( {"Type": FenrirEventType.stop_main_loop, "data": None} ) - sys.exit(0) + + def _safe_cleanup_process(self): + """Safely terminate the child process with timeout and fallback to SIGKILL.""" + if not hasattr(self, 'p_pid') or self.p_pid is None: + return + + try: + # Check if process is still alive + os.kill(self.p_pid, 0) # Signal 0 checks if process exists + except OSError: + # Process already dead + self.p_pid = None + return + + try: + # Try graceful termination first + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY screenDriver: Terminating process {self.p_pid} gracefully", + debug.DebugLevel.INFO + ) + os.kill(self.p_pid, signal.SIGTERM) + + # Wait up to 3 seconds for graceful termination + timeout = 3.0 + start_time = time.time() + while time.time() - start_time < timeout: + try: + os.kill(self.p_pid, 0) # Check if still alive + time.sleep(0.1) + except OSError: + # Process terminated gracefully + self.p_pid = None + return + + # Process didn't terminate gracefully, use SIGKILL + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY screenDriver: Process {self.p_pid} didn't terminate gracefully, using SIGKILL", + debug.DebugLevel.WARNING + ) + os.kill(self.p_pid, signal.SIGKILL) + time.sleep(0.5) # Give it a moment + + except OSError as e: + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY screenDriver: Error terminating process {self.p_pid}: {e}", + debug.DebugLevel.ERROR + ) + finally: + self.p_pid = None + + def _safe_cleanup_resources(self, old_attr=None): + """Safely clean up file descriptors and terminal attributes.""" + # Close output pipe safely + if hasattr(self, 'p_out') and self.p_out is not None: + try: + self.p_out.close() + self.env["runtime"]["DebugManager"].write_debug_out( + "PTY screenDriver: Closed output pipe", + debug.DebugLevel.INFO + ) + except Exception as e: + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY screenDriver: Error closing output pipe: {e}", + debug.DebugLevel.ERROR + ) + finally: + self.p_out = None + + # Restore terminal attributes safely + if old_attr is not None: + try: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_attr) + self.env["runtime"]["DebugManager"].write_debug_out( + "PTY screenDriver: Restored terminal attributes", + debug.DebugLevel.INFO + ) + except Exception as e: + self.env["runtime"]["DebugManager"].write_debug_out( + f"PTY screenDriver: Error restoring terminal attributes: {e}", + debug.DebugLevel.ERROR + ) def get_curr_application(self): pass diff --git a/src/fenrirscreenreader/screenDriver/vcsaDriver.py b/src/fenrirscreenreader/screenDriver/vcsaDriver.py index bdc95684..9c170f40 100644 --- a/src/fenrirscreenreader/screenDriver/vcsaDriver.py +++ b/src/fenrirscreenreader/screenDriver/vcsaDriver.py @@ -170,8 +170,9 @@ class driver(screenDriver): if screen is not None: use_screen = screen with open(use_screen, "w") as fd: - for c in text: - fcntl.ioctl(fd, termios.TIOCSTI, c) + text_bytes = text.encode('utf-8') + for byte in text_bytes: + fcntl.ioctl(fd, termios.TIOCSTI, bytes([byte])) def get_session_information(self): """Retrieve session information via D-Bus logind interface.