initial commit

This commit is contained in:
2023-08-22 02:04:00 +01:00
parent 7c0f3327f8
commit e200462b85
13 changed files with 222 additions and 0 deletions

View File

@@ -0,0 +1 @@
from .window import request_window_object as build

View File

@@ -0,0 +1,10 @@
import ctypes as ct
from pathlib import Path
bits = 64 if ct.sizeof(ct.c_voidp) == 8 else 32
controller_path = Path(__file__).parents[2].resolve() / "controllerClient"
DLL_PATH = controller_path / f"x{64 if bits == 64 else 86}" / f"nvdaControllerClient{bits}.dll"
libc = ct.CDLL(str(DLL_PATH))

View File

@@ -0,0 +1,26 @@
def _make_cache(vm) -> dict:
match vm.kind.name:
case "basic":
return {
**{f"BUTTON||strip {i} A1": vm.strip[i].A1 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} B1": vm.strip[i].B1 for i in range(vm.kind.num_strip)},
}
case "banana":
return {
**{f"BUTTON||strip {i} A1": vm.strip[i].A1 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} A2": vm.strip[i].A2 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} A3": vm.strip[i].A3 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} B1": vm.strip[i].B1 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} B2": vm.strip[i].B2 for i in range(vm.kind.num_strip)},
}
case "potato":
return {
**{f"BUTTON||strip {i} A1": vm.strip[i].A1 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} A2": vm.strip[i].A2 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} A3": vm.strip[i].A3 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} A4": vm.strip[i].A4 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} A5": vm.strip[i].A5 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} B1": vm.strip[i].B1 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} B2": vm.strip[i].B2 for i in range(vm.kind.num_strip)},
**{f"BUTTON||strip {i} B3": vm.strip[i].B3 for i in range(vm.kind.num_strip)},
}

View File

@@ -0,0 +1,29 @@
from .cdll import libc
class CBindings:
bind_test_if_running = libc.nvdaController_testIfRunning
bind_speak_text = libc.nvdaController_speakText
bind_cancel_speech = libc.nvdaController_cancelSpeech
bind_braille_message = libc.nvdaController_brailleMessage
def call(self, fn, *args, ok=(0,)):
retval = fn(*args)
if retval not in ok:
raise RuntimeError(f"{fn.__name__} returned {retval}")
return retval
class Nvda(CBindings):
@property
def is_running(self):
return self.call(self.bind_test_if_running, ok=(0, 1)) == 0
def speak(self, text):
self.call(self.bind_speak_text, text)
def cancel_speech(self):
self.call(self.bind_cancel_speech)
def braille_message(self, text):
self.call(self.bind_braille_message, text)

View File

@@ -0,0 +1,10 @@
from pyparsing import Group, OneOrMore, Optional, Suppress, Word, alphanums
class Parser:
def __init__(self):
self.widget = Group(OneOrMore(Word(alphanums)))
self.token = Suppress("||")
self.identifier = Group(OneOrMore(Word(alphanums)))
self.event = OneOrMore(Word(alphanums))
self.match = self.widget + self.token + self.identifier + Optional(self.token) + Optional(self.event)

View File

@@ -0,0 +1,60 @@
import PySimpleGUI as psg
from .models import _make_cache
from .nvda import Nvda
from .parser import Parser
class Window(psg.Window):
def __init__(self, title, vm):
self.vm = vm
self.kind = self.vm.kind
super().__init__(title, self.make_layout(), finalize=True)
self.cache = _make_cache(self.vm)
self.nvda = Nvda()
self.parser = Parser()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def make_layout(self) -> list:
"""Builds the window layout step by step"""
def add_physical_device_opts(layout):
devices = ["{type}: {name}".format(**self.vm.device.output(i)) for i in range(self.vm.device.outs)]
layout.append(
[
psg.Combo(
devices,
size=(22, 4),
expand_x=True,
enable_events=True,
key=f"DEVICE LIST||PHYSOUT {i}",
)
for i in range(self.kind.phys_out)
]
)
upper_layout = list()
[step(upper_layout) for step in (add_physical_device_opts,)]
row0 = psg.Frame("Hardware Out", upper_layout)
return [[row0]]
def run(self):
"""Runs the main window until an Close/Exit event"""
while True:
event, values = self.read()
if event in (psg.WIN_CLOSED, "Exit"):
break
match self.parser.match.parseString(event):
case _:
pass
def request_window_object(title, vm):
WINDOW_cls = Window
return WINDOW_cls(title, vm)