Files
fenrir/src/fenrirscreenreader/core/dynamicKeyboardLayoutMenu.py
2025-11-27 22:42:36 -05:00

204 lines
7.4 KiB
Python

#!/usr/bin/env python3
import glob
import os
from fenrirscreenreader.core import debug
class DynamicKeyboardLayoutCommand:
"""Dynamic command class for keyboard layout selection"""
def __init__(self, layoutName, layoutPath, env):
self.layoutName = layoutName
self.layoutPath = layoutPath
# Extract just the base name without extension for comparison
self.layoutBaseName = layoutName
self.env = env
def initialize(self, environment):
self.env = environment
def shutdown(self):
pass
def get_description(self):
return f"Set keyboard layout to {self.layoutName}"
def run(self):
try:
settingsManager = self.env["runtime"]["SettingsManager"]
currentLayout = settingsManager.get_setting(
"keyboard", "keyboardLayout"
)
# Check if already set (compare both full path and base name)
currentBaseName = os.path.splitext(os.path.basename(currentLayout))[0] if currentLayout else ""
if currentBaseName.lower() == self.layoutBaseName.lower() or currentLayout.lower() == self.layoutPath.lower():
self.env["runtime"]["OutputManager"].present_text(
f"Keyboard layout already set to {self.layoutName}"
)
return
# Set the new layout in the config file using full path
try:
# Update the setting in memory
settingsManager.set_setting(
"keyboard", "keyboardLayout", self.layoutPath
)
# Save to the actual config file
configFilePath = settingsManager.get_settings_file()
settingsManager.save_settings(configFilePath)
self.env["runtime"]["OutputManager"].present_text(
f"Keyboard layout set to {self.layoutName}. Please restart Fenrir for this change to take effect."
)
# Play accept sound
self.env["runtime"]["OutputManager"].present_text(
"", sound_icon="Accept", interrupt=False
)
except Exception as e:
self.env["runtime"]["OutputManager"].present_text(
f"Failed to change keyboard layout to {self.layoutName}"
)
self.env["runtime"]["DebugManager"].write_debug_out(
f"DynamicKeyboardLayout: Error setting layout {self.layoutName}: {e}",
debug.DebugLevel.ERROR,
)
# Play error sound
self.env["runtime"]["OutputManager"].present_text(
"", sound_icon="ErrorSound", interrupt=False
)
except Exception as e:
self.env["runtime"]["OutputManager"].present_text(
f"Keyboard layout change error: {str(e)}", interrupt=True
)
self.env["runtime"]["DebugManager"].write_debug_out(
f"DynamicKeyboardLayout: Unexpected error for {self.layoutName}: {e}",
debug.DebugLevel.ERROR,
)
def set_callback(self, callback):
pass
def add_dynamic_keyboard_layout_menus(VmenuManager):
"""Add dynamic keyboard layout menus to vmenu system"""
try:
env = VmenuManager.env
# Get keyboard layout files
layouts = get_keyboard_layouts(env)
if not layouts:
return
# Create keyboard layouts submenu
layoutMenu = {}
# Add layout commands
for layoutName, layoutPath in layouts:
layoutCommand = DynamicKeyboardLayoutCommand(
layoutName, layoutPath, env
)
layoutMenu[f"{layoutName} Action"] = layoutCommand
# Find keyboard menu in existing vmenu structure
# If fenrir menu exists, add layouts under it
if "fenrir Menu" in VmenuManager.menuDict:
fenrirMenu = VmenuManager.menuDict["fenrir Menu"]
if "keyboard Menu" in fenrirMenu:
# Add dynamic layouts to existing keyboard menu
keyboardMenu = fenrirMenu["keyboard Menu"]
keyboardMenu["Keyboard Layouts Menu"] = layoutMenu
else:
# Create keyboard menu with layouts
fenrirMenu["keyboard Menu"] = {
"Keyboard Layouts Menu": layoutMenu
}
else:
# Create standalone keyboard layouts menu
VmenuManager.menuDict["Keyboard Layouts Menu"] = layoutMenu
except Exception as e:
# Use debug manager for error logging
if "DebugManager" in env.get("runtime", {}):
env["runtime"]["DebugManager"].write_debug_out(
f"Error creating dynamic keyboard layout menus: {e}",
debug.DebugLevel.ERROR,
)
else:
print(f"Error creating dynamic keyboard layout menus: {e}")
def get_keyboard_layouts(env):
"""Get available keyboard layouts from keyboard directory"""
layouts = []
try:
# Get keyboard directory paths
keyboardDirs = []
# Check system installation path
systemKeyboardPath = "/etc/fenrirscreenreader/keyboard/"
if os.path.exists(systemKeyboardPath):
keyboardDirs.append(systemKeyboardPath)
# Check source/development path
try:
import fenrirscreenreader
fenrirPath = os.path.dirname(fenrirscreenreader.__file__)
devKeyboardPath = os.path.join(
fenrirPath, "..", "..", "config", "keyboard"
)
devKeyboardPath = os.path.abspath(devKeyboardPath)
if os.path.exists(devKeyboardPath):
keyboardDirs.append(devKeyboardPath)
except Exception:
pass
# Get current layout setting path
try:
currentLayoutSetting = env["runtime"]["SettingsManager"].get_setting(
"keyboard", "keyboardLayout"
)
if currentLayoutSetting and os.path.exists(currentLayoutSetting):
currentLayoutDir = os.path.dirname(currentLayoutSetting)
if currentLayoutDir not in keyboardDirs:
keyboardDirs.append(currentLayoutDir)
except Exception:
pass
# Scan for .conf files
seenLayouts = set()
for keyboardDir in keyboardDirs:
try:
confFiles = glob.glob(os.path.join(keyboardDir, "*.conf"))
for confFile in confFiles:
layoutName = os.path.splitext(os.path.basename(confFile))[
0
]
if layoutName not in seenLayouts:
seenLayouts.add(layoutName)
layouts.append((layoutName, confFile))
except Exception as e:
if "DebugManager" in env.get("runtime", {}):
env["runtime"]["DebugManager"].write_debug_out(
f"Error scanning keyboard directory {keyboardDir}: {e}",
debug.DebugLevel.WARNING,
)
# Sort layouts alphabetically
layouts.sort(key=lambda x: x[0].lower())
except Exception as e:
if "DebugManager" in env.get("runtime", {}):
env["runtime"]["DebugManager"].write_debug_out(
f"Error getting keyboard layouts: {e}",
debug.DebugLevel.ERROR,
)
return layouts