From 75e6168f9914d71f8f3a97a052ef927a7ca1c2f7 Mon Sep 17 00:00:00 2001 From: brian crabtree Date: Wed, 14 Apr 2021 18:40:45 -0400 Subject: [PATCH 01/11] pydfu integration --- setup.py | 2 + src/druid/cli.py | 29 +++ src/druid/pydfu.py | 584 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 615 insertions(+) create mode 100644 src/druid/pydfu.py diff --git a/setup.py b/setup.py index 1d60811..72c77f6 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,8 @@ "Click>=7.0", "prompt-toolkit>=2.0.10,<3.0", "pyserial>=3.4", + "pyusb", + "requests", "setuptools", "setuptools_scm", "setuptools_scm_git_archive", diff --git a/src/druid/cli.py b/src/druid/cli.py index 74bb6e2..01ff786 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -5,9 +5,12 @@ import click +import requests + from druid import __version__ from druid.crow import Crow from druid import repl as druid_repl +from druid import pydfu @click.group(invoke_without_command=True) @click.pass_context @@ -44,6 +47,32 @@ def upload(filename): time.sleep(0.3) click.echo(crow.read(1000000)) +@cli.command(short_help="Update bootloader") +def update(): + print("update") + v = requests.get('https://raw.githubusercontent.com/monome/druid/master/README.md') + print(v.text) + + """ + Update bootloader + + with Crow() as crow: + crow.connect() + crow.write('^^b') + time.sleep(1.0) + print("crow bootloader enabled") + try: + pydfu.init() + except ValueError: + print("pydfu didn't find crow") + exit() + print("Writing binary...") + pydfu.write_bin("crow.bin", progress=pydfu.cli_progress) + print("Exiting DFU...") + pydfu.exit_dfu() + """ + + @cli.command() @click.argument("filename", type=click.Path(exists=True), required=False) def repl(filename): diff --git a/src/druid/pydfu.py b/src/druid/pydfu.py new file mode 100644 index 0000000..2802d26 --- /dev/null +++ b/src/druid/pydfu.py @@ -0,0 +1,584 @@ +#!/usr/bin/env python2 +# This file is part of the OpenMV project. +# +# Copyright (c) 2013-2021 Ibrahim Abdelkader +# Copyright (c) 2013-2021 Kwabena W. Agyeman +# +# This work is licensed under the MIT license, see the file LICENSE for details. +# +# This module implements the DFU protocol for STM32 chips. +# See app note AN3156 for a description of the DFU protocol. +# See document UM0391 for a dscription of the DFuse file. + +from __future__ import print_function + +import argparse +import re +import struct +import sys +import usb.core +import usb.util +import zlib +import os +import time + +# VID/PID +__VID = 0x0483 +__PID = 0xdf11 + +# USB request __TIMEOUT +__TIMEOUT = 5000 + +# DFU commands +__DFU_DETACH = 0 +__DFU_DNLOAD = 1 +__DFU_UPLOAD = 2 +__DFU_GETSTATUS = 3 +__DFU_CLRSTATUS = 4 +__DFU_GETSTATE = 5 +__DFU_ABORT = 6 + +# DFU status +__DFU_STATE_APP_IDLE = 0x00 +__DFU_STATE_APP_DETACH = 0x01 +__DFU_STATE_DFU_IDLE = 0x02 +__DFU_STATE_DFU_DOWNLOAD_SYNC = 0x03 +__DFU_STATE_DFU_DOWNLOAD_BUSY = 0x04 +__DFU_STATE_DFU_DOWNLOAD_IDLE = 0x05 +__DFU_STATE_DFU_MANIFEST_SYNC = 0x06 +__DFU_STATE_DFU_MANIFEST = 0x07 +__DFU_STATE_DFU_MANIFEST_WAIT_RESET = 0x08 +__DFU_STATE_DFU_UPLOAD_IDLE = 0x09 +__DFU_STATE_DFU_ERROR = 0x0a + +__DFU_STATUS = [ + "DFU_STATE_APP_IDLE", + "DFU_STATE_APP_DETACH", + "DFU_STATE_DFU_IDLE", + "DFU_STATE_DFU_DOWNLOAD_SYNC", + "DFU_STATE_DFU_DOWNLOAD_BUSY", + "DFU_STATE_DFU_DOWNLOAD_IDLE", + "DFU_STATE_DFU_MANIFEST_SYNC", + "DFU_STATE_DFU_MANIFEST", + "DFU_STATE_DFU_MANIFEST_WAIT_RESET", + "DFU_STATE_DFU_UPLOAD_IDLE", + "DFU_STATE_DFU_ERROR" +] + +_DFU_DESCRIPTOR_TYPE = 0x21 + +# USB device handle +__dev = None + +__verbose = None + +# USB DFU interface +__DFU_INTERFACE = 0 + +import inspect +if 'length' in inspect.getargspec(usb.util.get_string).args: + # PyUSB 1.0.0.b1 has the length argument + def get_string(dev, index): + return usb.util.get_string(dev, 255, index) +else: + # PyUSB 1.0.0.b2 dropped the length argument + def get_string(dev, index): + return usb.util.get_string(dev, index) + + +def init(): + """Initializes the found DFU device so that we can program it.""" + global __dev + devices = get_dfu_devices(idVendor=__VID, idProduct=__PID) + if not devices: + raise ValueError('No DFU device found') + if len(devices) > 1: + raise ValueError("Multiple DFU devices found") + __dev = devices[0] + + # Claim DFU interface + usb.util.claim_interface(__dev, __DFU_INTERFACE) + + # Clear status + clr_status() + +def clr_status(): + """Clears any error status (perhaps left over from a previous session).""" + while (get_status() != __DFU_STATE_DFU_IDLE): + __dev.ctrl_transfer(0x21, __DFU_CLRSTATUS, 0, __DFU_INTERFACE, None, __TIMEOUT) + time.sleep(0.100) + + +def get_status(): + """Get the status of the last operation.""" + stat = __dev.ctrl_transfer(0xA1, __DFU_GETSTATUS, 0, __DFU_INTERFACE, 6, 20000) + #print ("DFU Status: ", __DFU_STATUS[stat[4]]) + return stat[4] + + +def mass_erase(): + """Performs a MASS erase (i.e. erases the entire device.""" + # Send DNLOAD with first byte=0x41 + __dev.ctrl_transfer(0x21, __DFU_DNLOAD, 0, __DFU_INTERFACE, + "\x41", __TIMEOUT) + + # Execute last command + if get_status() != __DFU_STATE_DFU_DOWNLOAD_BUSY: + raise Exception("DFU: erase failed") + + # Check command state + if get_status() != __DFU_STATE_DFU_DOWNLOAD_IDLE: + raise Exception("DFU: erase failed") + + +def page_erase(addr): + """Erases a single page.""" + if __verbose: + print("Erasing page: 0x%x..." % (addr)) + + # Send DNLOAD with first byte=0x41 and page address + buf = struct.pack(" 0: + write_size = size + if not mass_erase_used: + for segment in mem_layout: + if addr >= segment['addr'] and \ + addr <= segment['last_addr']: + # We found the page containing the address we want to + # write, erase it + page_size = segment['page_size'] + page_addr = addr & ~(page_size - 1) + if addr + write_size > page_addr + page_size: + write_size = page_addr + page_size - addr + page_erase(page_addr) + break + write_memory(addr, data[:write_size], progress, + elem_addr, elem_size) + data = data[write_size:] + addr += write_size + size -= write_size + if progress: + progress(elem_addr, addr - elem_addr, elem_size) + +def write_bin(path, progress=None): + try: + with open(path, 'rb') as f: + buf = f.read() + except Exception as e: + print(e) + return + + print("file opened") + + xfer_bytes = 0 + xfer_total = len(buf) + + while xfer_bytes < xfer_total: + # Send chunk + chunk = min (64, xfer_total-xfer_bytes) + write_page(buf[xfer_bytes:xfer_bytes+chunk], xfer_bytes) + xfer_bytes += chunk + if (progress): + progress(0x08000000+xfer_bytes, xfer_bytes, xfer_total) + +def cli_progress(addr, offset, size): + """Prints a progress report suitable for use on the command line.""" + width = 25 + done = offset * width // size + print("\r0x{:08x} {:7d} [{}{}] {:3d}% " + .format(addr, size, '=' * done, ' ' * (width - done), + offset * 100 // size), end="") + sys.stdout.flush() + if offset == size: + print("") + + +def main(): + """Test program for verifying this files functionality.""" + global __verbose + # Parse CMD args + parser = argparse.ArgumentParser(description='DFU Python Util') + #parser.add_argument("path", help="file path") + parser.add_argument( + "-l", "--list", + help="list available DFU devices", + action="store_true", + default=False + ) + parser.add_argument( + "-m", "--mass-erase", + help="mass erase device", + action="store_true", + default=False + ) + parser.add_argument( + "-u", "--upload", + help="read file from DFU device", + dest="path", + default=False + ) + parser.add_argument( + "-v", "--verbose", + help="increase output verbosity", + action="store_true", + default=False + ) + args = parser.parse_args() + + __verbose = args.verbose + + if args.list: + list_dfu_devices(idVendor=__VID, idProduct=__PID) + return + + init() + + if args.mass_erase: + print ("Mass erase...") + mass_erase() + + if args.path: + ext = os.path.splitext(args.path)[1] + if ext == ".bin": + print("Writing binary...") + write_bin(args.path, progress=cli_progress) + + print("Exiting DFU...") + exit_dfu() + elif (ext == '.dfu'): + elements = read_dfu_file(args.path) + if not elements: + return + print("Writing memory...") + write_elements(elements, args.mass_erase, progress=cli_progress) + + print("Exiting DFU...") + exit_dfu() + else: + print("File format not supported!") + + return + + print("No command specified") + +if __name__ == '__main__': + main() From 34c186fb32670ea3b54359918e7ddd4967d2c113 Mon Sep 17 00:00:00 2001 From: brian crabtree Date: Wed, 14 Apr 2021 19:06:43 -0400 Subject: [PATCH 02/11] fetch version, comment next steps --- src/druid/cli.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/druid/cli.py b/src/druid/cli.py index 01ff786..37687d5 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -49,15 +49,22 @@ def upload(filename): @cli.command(short_help="Update bootloader") def update(): + """ Update bootloader + """ print("update") - v = requests.get('https://raw.githubusercontent.com/monome/druid/master/README.md') - print(v.text) + v = requests.get('https://raw.githubusercontent.com/monome/crow/main/version.txt') + r = v.text.split() + print("version", r[0]) + print(r[1]) """ - Update bootloader - with Crow() as crow: crow.connect() + # get version + # compare to remote version (quit if up to date) + # download file + # unpack file + # flash file (below) crow.write('^^b') time.sleep(1.0) print("crow bootloader enabled") From fa0060a6c904866594b17c396e929f857274495f Mon Sep 17 00:00:00 2001 From: brian crabtree Date: Thu, 15 Apr 2021 17:05:54 -0400 Subject: [PATCH 03/11] done/works --- setup.py | 1 + src/druid/cli.py | 71 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/setup.py b/setup.py index 72c77f6..71fdfb8 100644 --- a/setup.py +++ b/setup.py @@ -35,6 +35,7 @@ "setuptools", "setuptools_scm", "setuptools_scm_git_archive", + "wget", ], extras_require={ "test": [ diff --git a/src/druid/cli.py b/src/druid/cli.py index 37687d5..b1d0992 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -6,6 +6,8 @@ import click import requests +import wget +import os from druid import __version__ from druid.crow import Crow @@ -51,33 +53,64 @@ def upload(filename): def update(): """ Update bootloader """ - print("update") - v = requests.get('https://raw.githubusercontent.com/monome/crow/main/version.txt') - r = v.text.split() - print("version", r[0]) - print(r[1]) + print("Checking for updates...") + git_query = requests.get('https://raw.githubusercontent.com/monome/crow/main/version.txt') + git_data = git_query.text.split() + print(">> git version", git_data[0]) - """ with Crow() as crow: - crow.connect() - # get version - # compare to remote version (quit if up to date) - # download file - # unpack file - # flash file (below) - crow.write('^^b') - time.sleep(1.0) - print("crow bootloader enabled") + local_version = "none" + try: + crow.connect() + except: + print("No crow found, or might be in bootloader mode already...") + local_version = "0" + + # crow found: clear script and read version + if local_version != "0": + crow.write("^^c") + time.sleep(1.0) + c = crow.read(1000000) + crow.write("^^v") + tmp = (crow.read(100)).split("'") + local_version = tmp[1][1:] + + print(">> local version: ", local_version) + + if local_version >= git_data[0]: + print("Up to date.") + exit() + + # delete old crow.dfu if exists + if os.path.exists("crow.dfu"): + os.remove("crow.dfu") + + print("Downloading new version:", git_data[1]) + wget.download(git_data[1]) + print("\n") + + if local_version != "0": + crow.write('^^b') + time.sleep(1.0) + print("Crow bootloader enabled.") + try: pydfu.init() except ValueError: - print("pydfu didn't find crow") + print("Error: pydfu didn't find crow!") exit() - print("Writing binary...") - pydfu.write_bin("crow.bin", progress=pydfu.cli_progress) + + elements = pydfu.read_dfu_file("crow.dfu") + if not elements: + return + print("Writing memory...") + pydfu.write_elements(elements, True, progress=pydfu.cli_progress) + print("Exiting DFU...") pydfu.exit_dfu() - """ + + os.remove("crow.dfu") + print("Update complete.") @cli.command() From 7dab7598d40fa7b48f92393cafcdff207bad5187 Mon Sep 17 00:00:00 2001 From: brian crabtree Date: Mon, 19 Apr 2021 13:44:37 -0400 Subject: [PATCH 04/11] fixes and suggestions --- setup.py | 2 +- src/druid/cli.py | 13 +++++++------ src/druid/pydfu.py | 3 +++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 71fdfb8..4321678 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ ], install_requires=[ "Click>=7.0", + "packaging", "prompt-toolkit>=2.0.10,<3.0", "pyserial>=3.4", "pyusb", @@ -35,7 +36,6 @@ "setuptools", "setuptools_scm", "setuptools_scm_git_archive", - "wget", ], extras_require={ "test": [ diff --git a/src/druid/cli.py b/src/druid/cli.py index b1d0992..6cd742a 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -6,8 +6,8 @@ import click import requests -import wget import os +from packaging import version from druid import __version__ from druid.crow import Crow @@ -49,9 +49,9 @@ def upload(filename): time.sleep(0.3) click.echo(crow.read(1000000)) -@cli.command(short_help="Update bootloader") +@cli.command(short_help="Update crow firmware") def update(): - """ Update bootloader + """ Update crow firmware """ print("Checking for updates...") git_query = requests.get('https://raw.githubusercontent.com/monome/crow/main/version.txt') @@ -77,7 +77,7 @@ def update(): print(">> local version: ", local_version) - if local_version >= git_data[0]: + if version.parse(local_version) >= version.parse(git_data[0]): print("Up to date.") exit() @@ -86,8 +86,9 @@ def update(): os.remove("crow.dfu") print("Downloading new version:", git_data[1]) - wget.download(git_data[1]) - print("\n") + res = requests.get(git_data[1]) + with open('crow.dfu', 'wb') as fwfile: + fwfile.write(res.content) if local_version != "0": crow.write('^^b') diff --git a/src/druid/pydfu.py b/src/druid/pydfu.py index 2802d26..559cd6e 100644 --- a/src/druid/pydfu.py +++ b/src/druid/pydfu.py @@ -1,6 +1,9 @@ #!/usr/bin/env python2 # This file is part of the OpenMV project. # +# https://github.com/openmv/openmv +# /tools/pydfu.py +# # Copyright (c) 2013-2021 Ibrahim Abdelkader # Copyright (c) 2013-2021 Kwabena W. Agyeman # From 6ab180e4e67c98454765b639dc82d90b2929876d Mon Sep 17 00:00:00 2001 From: brian crabtree Date: Mon, 19 Apr 2021 16:07:53 -0400 Subject: [PATCH 05/11] crow reset --- src/druid/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/druid/cli.py b/src/druid/cli.py index 6cd742a..2be1175 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -68,8 +68,8 @@ def update(): # crow found: clear script and read version if local_version != "0": - crow.write("^^c") - time.sleep(1.0) + crow.write("crow.reset()") + time.sleep(0.1) c = crow.read(1000000) crow.write("^^v") tmp = (crow.read(100)).split("'") From 77f4154e376a1e115f287384ea8b92813d601801 Mon Sep 17 00:00:00 2001 From: brian crabtree Date: Thu, 29 Apr 2021 17:27:31 -0400 Subject: [PATCH 06/11] rename update command to firmwrae, change detection to product string --- src/druid/cli.py | 2 +- src/druid/crow.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/druid/cli.py b/src/druid/cli.py index 2be1175..fffb7e3 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -50,7 +50,7 @@ def upload(filename): click.echo(crow.read(1000000)) @cli.command(short_help="Update crow firmware") -def update(): +def firmware(): """ Update crow firmware """ print("Checking for updates...") diff --git a/src/druid/crow.py b/src/druid/crow.py index b4ad000..10d09db 100644 --- a/src/druid/crow.py +++ b/src/druid/crow.py @@ -10,11 +10,11 @@ logger = logging.getLogger(__name__) -def find_serial_port(hwid): +def find_serial_port(): for portinfo in serial.tools.list_ports.comports(): - if hwid in portinfo.hwid: + if "crow" in portinfo.product: return portinfo - raise DeviceNotFoundError(f"can't find device {hwid}") + raise DeviceNotFoundError(f"can't find crow device") class Crow: def __init__(self, serial=None): @@ -23,7 +23,7 @@ def __init__(self, serial=None): self.event_handlers = {} def find_device(self): - portinfo = find_serial_port('USB VID:PID=0483:5740') + portinfo = find_serial_port() try: return serial.Serial( portinfo.device, @@ -62,7 +62,7 @@ def raise_event(self, event, *args, **kwargs): def replace_handlers(self, handlers): self.event_handlers = handlers - + def reconnect(self, err_event=False): try: self.connect() From e522e3e1e0afc4b72c4bda1912358ebe33782d86 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 5 May 2021 07:16:01 -0700 Subject: [PATCH 07/11] unmark MassErase flag. This was causing update to fail --- src/druid/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/druid/cli.py b/src/druid/cli.py index fffb7e3..dc6f5a1 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -105,7 +105,7 @@ def firmware(): if not elements: return print("Writing memory...") - pydfu.write_elements(elements, True, progress=pydfu.cli_progress) + pydfu.write_elements(elements, False, progress=pydfu.cli_progress) print("Exiting DFU...") pydfu.exit_dfu() From 30b353d2cdee27a2483558f4eca0b5ea38a84b4b Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 5 May 2021 07:16:27 -0700 Subject: [PATCH 08/11] add 'clearscript' command to replicate the bash script --- src/druid/cli.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/druid/cli.py b/src/druid/cli.py index dc6f5a1..b620a6d 100644 --- a/src/druid/cli.py +++ b/src/druid/cli.py @@ -114,6 +114,21 @@ def firmware(): print("Update complete.") +@cli.command(short_help="Clear userscript") +def clearscript(): + """ Clear userscript from crow' flash memory """ + try: + pydfu.init() + except ValueError: + print("Error: pydfu didn't find crow! Check you've forced the bootloader.") + exit() + print("Clearing userscript...") + # note we must write a single byte of 0x00 but are primarily just triggering erase of the flash page + pydfu.write_elements([{"addr":0x08010000, "size": 1, "data": [0]}], False, progress=pydfu.cli_progress) + print("Complete. Exiting DFU...") + pydfu.exit_dfu() + + @cli.command() @click.argument("filename", type=click.Path(exists=True), required=False) def repl(filename): From c3437dba9e0515532aa06557a31275036fec6e4c Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 5 May 2021 08:11:57 -0700 Subject: [PATCH 09/11] add support for public.view and hide other public related ^^ commands --- src/druid/repl.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/druid/repl.py b/src/druid/repl.py index aa8824e..40db5fa 100644 --- a/src/druid/repl.py +++ b/src/druid/repl.py @@ -214,6 +214,10 @@ def build_ui(self): self.captures = [ TextArea(style='class:capture-field', height=2), TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), ] self.output_field = TextArea( style='class:output-field', @@ -297,8 +301,23 @@ def crow_event(self, line, event, args): ch = int(ch_str) if ch >= 1 and ch <= 2: self.output_to_field(self.captures[ch - 1], f'\ninput[{ch}] = {val}\n') + elif event == 'pubview': + io, ch_str, val = args + ch = int(ch_str) + if io == "'input'": + if ch >= 1 and ch <= 2: + self.output_to_field(self.captures[ch - 1], f'\nin[{ch}] = {val}\n') + elif io == "'output'": + if ch >= 1 and ch <= 4: + self.output_to_field(self.captures[ch + 1], f'\nout[{ch}] = {val}\n') + else: + self.output_to_field(self.captures[0], f'\nERR:{io}[{ch}] = {val}\n') + elif event == 'ready' or event == 'pupdate' or event == 'pub': + args = args # NOP + # ignore for now + # TODO ^^ready triggers public.discover() and capture public vars into a modifiable form else: - self.output(f'^^{event}({", ".join(args)})') + self.output(f'^^{event}({", ".join(args)})\n') # these come from: # https://github.com/prompt-toolkit/python-prompt-toolkit/blob/5c3d13eb849885bc4c1a2553ea6f81e6272f84c9/prompt_toolkit/key_binding/bindings/scroll.py#L147 From 1cf5186a40b7a71b5edba1f1118cf3f67f33fb98 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 5 May 2021 08:14:01 -0700 Subject: [PATCH 10/11] Revert "add support for public.view and hide other public related ^^ commands" This reverts commit c3437dba9e0515532aa06557a31275036fec6e4c. --- src/druid/repl.py | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/druid/repl.py b/src/druid/repl.py index 40db5fa..aa8824e 100644 --- a/src/druid/repl.py +++ b/src/druid/repl.py @@ -214,10 +214,6 @@ def build_ui(self): self.captures = [ TextArea(style='class:capture-field', height=2), TextArea(style='class:capture-field', height=2), - TextArea(style='class:capture-field', height=2), - TextArea(style='class:capture-field', height=2), - TextArea(style='class:capture-field', height=2), - TextArea(style='class:capture-field', height=2), ] self.output_field = TextArea( style='class:output-field', @@ -301,23 +297,8 @@ def crow_event(self, line, event, args): ch = int(ch_str) if ch >= 1 and ch <= 2: self.output_to_field(self.captures[ch - 1], f'\ninput[{ch}] = {val}\n') - elif event == 'pubview': - io, ch_str, val = args - ch = int(ch_str) - if io == "'input'": - if ch >= 1 and ch <= 2: - self.output_to_field(self.captures[ch - 1], f'\nin[{ch}] = {val}\n') - elif io == "'output'": - if ch >= 1 and ch <= 4: - self.output_to_field(self.captures[ch + 1], f'\nout[{ch}] = {val}\n') - else: - self.output_to_field(self.captures[0], f'\nERR:{io}[{ch}] = {val}\n') - elif event == 'ready' or event == 'pupdate' or event == 'pub': - args = args # NOP - # ignore for now - # TODO ^^ready triggers public.discover() and capture public vars into a modifiable form else: - self.output(f'^^{event}({", ".join(args)})\n') + self.output(f'^^{event}({", ".join(args)})') # these come from: # https://github.com/prompt-toolkit/python-prompt-toolkit/blob/5c3d13eb849885bc4c1a2553ea6f81e6272f84c9/prompt_toolkit/key_binding/bindings/scroll.py#L147 From f57e0e643062469d942835920d35623563e126dc Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 5 May 2021 08:11:57 -0700 Subject: [PATCH 11/11] add support for public.view and hide other public related ^^ commands --- src/druid/repl.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/druid/repl.py b/src/druid/repl.py index aa8824e..40db5fa 100644 --- a/src/druid/repl.py +++ b/src/druid/repl.py @@ -214,6 +214,10 @@ def build_ui(self): self.captures = [ TextArea(style='class:capture-field', height=2), TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), + TextArea(style='class:capture-field', height=2), ] self.output_field = TextArea( style='class:output-field', @@ -297,8 +301,23 @@ def crow_event(self, line, event, args): ch = int(ch_str) if ch >= 1 and ch <= 2: self.output_to_field(self.captures[ch - 1], f'\ninput[{ch}] = {val}\n') + elif event == 'pubview': + io, ch_str, val = args + ch = int(ch_str) + if io == "'input'": + if ch >= 1 and ch <= 2: + self.output_to_field(self.captures[ch - 1], f'\nin[{ch}] = {val}\n') + elif io == "'output'": + if ch >= 1 and ch <= 4: + self.output_to_field(self.captures[ch + 1], f'\nout[{ch}] = {val}\n') + else: + self.output_to_field(self.captures[0], f'\nERR:{io}[{ch}] = {val}\n') + elif event == 'ready' or event == 'pupdate' or event == 'pub': + args = args # NOP + # ignore for now + # TODO ^^ready triggers public.discover() and capture public vars into a modifiable form else: - self.output(f'^^{event}({", ".join(args)})') + self.output(f'^^{event}({", ".join(args)})\n') # these come from: # https://github.com/prompt-toolkit/python-prompt-toolkit/blob/5c3d13eb849885bc4c1a2553ea6f81e6272f84c9/prompt_toolkit/key_binding/bindings/scroll.py#L147