From 597b007333d8ec0d9cfd29e6941fcbe57379108a Mon Sep 17 00:00:00 2001 From: Sébastien Dailly Date: Mon, 22 May 2023 08:40:47 +0200 Subject: Initial commit --- src/Makefile | 9 + src/actions.py | 72 ++++ src/boot.py | 20 + src/code.py | 158 ++++++++ src/keyboard_layout_win_frnb.py | 580 ++++++++++++++++++++++++++++ src/keycode_win_frnb.py | 165 ++++++++ src/lib/adafruit_bitmap_font/__init__.py | 0 src/lib/adafruit_display_shapes/__init__.py | 0 src/macros/1-ff.py | 83 ++++ src/macros/2-num.py | 103 +++++ src/macros/3-fkeys.py | 87 +++++ src/macros/4-move_mouse.py | 54 +++ src/macros/5-irssi.py | 49 +++ src/macros/99-none.py | 3 + src/macros/99-wm.py | 62 +++ src/macros/olympia.py | 91 +++++ src/macros/santana.py | 47 +++ src/macros/teams.py | 44 +++ src/macros/zim.py | 53 +++ src/menu.py | 23 ++ src/readme.rst | 73 ++++ src/skeleton.py | 199 ++++++++++ src/ticks.py | 21 + 23 files changed, 1996 insertions(+) create mode 100644 src/Makefile create mode 100644 src/actions.py create mode 100644 src/boot.py create mode 100644 src/code.py create mode 100644 src/keyboard_layout_win_frnb.py create mode 100644 src/keycode_win_frnb.py create mode 100644 src/lib/adafruit_bitmap_font/__init__.py create mode 100644 src/lib/adafruit_display_shapes/__init__.py create mode 100644 src/macros/1-ff.py create mode 100644 src/macros/2-num.py create mode 100644 src/macros/3-fkeys.py create mode 100644 src/macros/4-move_mouse.py create mode 100644 src/macros/5-irssi.py create mode 100644 src/macros/99-none.py create mode 100644 src/macros/99-wm.py create mode 100644 src/macros/olympia.py create mode 100644 src/macros/santana.py create mode 100644 src/macros/teams.py create mode 100644 src/macros/zim.py create mode 100644 src/menu.py create mode 100644 src/readme.rst create mode 100644 src/skeleton.py create mode 100644 src/ticks.py (limited to 'src') diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ecb9a82 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,9 @@ +.phony: all +all: skeleton.mpy keycode_win_frnb.mpy actions.mpy + +%.mpy: %.py + .././mpy-cross.static-amd64-linux-8.0.3 $< + +.phony: clean +clean: + rm *.mpy diff --git a/src/actions.py b/src/actions.py new file mode 100644 index 0000000..1e40bbd --- /dev/null +++ b/src/actions.py @@ -0,0 +1,72 @@ +from supervisor import ticks_ms +import ticks +import time +from random import randrange + +class Action(object): + def __new__(cls): + if not hasattr(cls, 'instance'): + cls.instance = super(Action, cls).__new__(cls) + cls.instance.events = [] + return cls.instance + + + def __init__(self): + """ Do nothing and do not get any constructor. + This is intended because it allow to call the class directly like : + Action().key( … ) + """ + pass + + def set_macropad(self, macropad): + self.macropad = macropad + + def key(self, pressed, sequence): + """ Send a key to the host + """ + + if pressed: + action = self.macropad.keyboard.press + s = sequence + else: + action = self.macropad.keyboard.release + s = reversed(sequence) + for item in s: + action(item) + + def sequence(self, keys): + """ Send a sequence of keys to the host + """ + for key in keys: + self.key(True, key) + time.sleep( randrange(25, 74)/400. ) + self.key(False, key) + + def delay(self, duration, callback): + "Register a delayed task" + time = ticks_ms() + next_tick = ticks.add( + ticks_ms(), + duration + ) + self.events.append((next_tick, callback)) + + def flash(self, key, release_color): + "Flash the key with a high light for 10ms" + def restore(): + if self.macropad.pixels[key] == (127, 127, 127): + self.macropad.pixels[key] = release_color + self.macropad.pixels.show() + self.delay(10, restore) + self.macropad.pixels[key] = (127, 127, 127) + + def collect_tasks(self): + "Execute all the delayed tasks" + remain = [] + time = ticks_ms() + for event in self.events: + if not ticks.less(event[0]): + event[1]() + else: + remain.append(event) + self.events = remain diff --git a/src/boot.py b/src/boot.py new file mode 100644 index 0000000..0a30e17 --- /dev/null +++ b/src/boot.py @@ -0,0 +1,20 @@ +# This example is for the MacroPad, +# or any board with buttons that are connected to ground when pressed. + +import usb_cdc +import storage +import board, digitalio + +# On the Macropad, pressing a key grounds it. You need to set a pull-up. +# If not pressed, the key will be at +V (due to the pull-up). +# The KEY12 is the RightBotom key in the keypad +button = digitalio.DigitalInOut(board.KEY12) +button.pull = digitalio.Pull.UP + +usb_cdc.enable(console=True, data=True) +# Disable devices only if button is not pressed. +if button.value: + print("KEY12 Unpressed, starting without storage") + storage.disable_usb_drive() +else: + print("Mounting USB storage") diff --git a/src/code.py b/src/code.py new file mode 100644 index 0000000..813574a --- /dev/null +++ b/src/code.py @@ -0,0 +1,158 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +A macro/hotkey program for Adafruit MACROPAD. Macro setups are stored in the +/macros folder (configurable below), load up just the ones you're likely to +use. Plug into computer's USB port, use dial to select an application macro +set, press MACROPAD keys to send key sequences and other USB protocols. +""" + +# pylint: disable=import-error, unused-import, too-few-public-methods + +import os +import time +import displayio +import terminalio + +import usb_cdc +import json + +#from adafruit_display_shapes.rect import Rect +#from adafruit_display_text import label +from adafruit_macropad import MacroPad + +from supervisor import runtime + +from actions import Action +import skeleton +import menu + +# CONFIGURABLES ------------------------ + +MACRO_FOLDER = '/macros' + + +# INITIALIZATION ----------------------- + +# Do not reload the application when the files are changed +runtime.autoreload = False + +macropad = MacroPad() +macropad.display.auto_refresh = False +macropad.pixels.auto_write = False + +Action().set_macropad(macropad) + +# Load all the macro key setups from .py files in MACRO_FOLDER +apps = [] +files = os.listdir(MACRO_FOLDER) +files.sort() +for filename in files: + if filename.startswith('._'): + continue + if filename.endswith('.py'): + module_name = filename[:-3] + elif filename.endswith('.mpy'): + module_name = filename[:-4] + else: + continue + + try: + module = __import__(MACRO_FOLDER + '/' + module_name) + apps.append(module.configuration) + except (SyntaxError, ImportError, AttributeError, KeyError, NameError, + IndexError, TypeError) as err: + print("ERROR in", filename) + import traceback + traceback.print_exception(err, err, err.__traceback__) + +if not apps: + group[13].text = 'NO MACRO FILES FOUND' + macropad.display.refresh() + while True: + pass + +app_index = 0 + +def run_application(app): + # Read encoder position. If it's changed, declare it as a button. + position = app.macropad.encoder + if position > app.position: + app.position = position + key_number = 12 + pressed = True + elif position < app.position: + app.position = position + key_number = 13 + pressed = True + else: + event = app.macropad.keys.events.get() + if not event: + app.tick() + return # No key events + key_number = event.key_number + pressed = event.pressed + + return app.execute_action(key_number, pressed or False) + +def switch_layout(configuration): + global app + app = skeleton.Handler(macropad, configuration) + app.start() + + +def search_and_switch(layout): + found_application = False + for local_app in apps: + if local_app.title == layout and app is not local_app: + found_application = True + switch_layout(local_app) + break + +def send_layout(_any): + if usb_cdc.data.connected: + usb_cdc.data.write(bytes("%s\n" % app.name, "utf-8")) + +actions = { + "layout" : search_and_switch, + "get_layout" : send_layout, + } + +switch_layout(apps[app_index]) + +# MAIN LOOP ---------------------------- + +while True: + + # First read from the serial + # If we have an application name from the command line, switch to this app. + if usb_cdc.data.connected and usb_cdc.data.in_waiting != 0: + line = usb_cdc.data.read(usb_cdc.data.in_waiting).strip() + commands = json.loads(line) + for command in commands: + for key, value in command.items(): + actions[key](value) + continue + + # Handle encoder button. If state has changed, and if there's a + # corresponding macro, set up variables to act on this just like + # the keypad keys, as if it were a 13th key/macro. + app.macropad.encoder_switch_debounced.update() + encoder_switch = app.macropad.encoder_switch_debounced.pressed + # On the encoder button, switch to the next mode + if encoder_switch: + + config = menu.build_application(apps) + menu_app = skeleton.Handler(macropad, config) + menu_app.start() + while True: + configuration = run_application(menu_app) + if configuration is None: continue + switch_layout(configuration) + break + send_layout(None) + + run_application(app) + continue diff --git a/src/keyboard_layout_win_frnb.py b/src/keyboard_layout_win_frnb.py new file mode 100644 index 0000000..51d8bc2 --- /dev/null +++ b/src/keyboard_layout_win_frnb.py @@ -0,0 +1,580 @@ +# SPDX-FileCopyrightText: 2022 Neradoc NeraOnGit@ri1.fr +# SPDX-License-Identifier: MIT +""" +This file was automatically generated using Circuitpython_Keyboard_Layouts +""" +from adafruit_hid.keyboard_layout_base import KeyboardLayoutBase + + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/Neradoc/Circuitpython_Keyboard_Layouts.git" + + +class KeyboardLayout(KeyboardLayoutBase): + ASCII_TO_KEYCODE = ( + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x2a' # BACKSPACE + b'\x2b' # '\t' + b'\x28' # '\n' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x29' # ESC + b'\x00' + b'\x00' + b'\x00' + b'\x00' + b'\x2c' # ' ' + b'\x9c' # '!' + b'\x1e' # '"' + b'\xb5' # '#' + b'\x35' # '$' + b'\x2e' # '%' + b'\x08' # '&' + b'\x0a' # "'" + b'\x21' # '(' + b'\x22' # ')' + b'\x27' # '*' + b'\x24' # '+' + b'\x0a' # ',' + b'\x00' # '-' (Dead key) + b'\x00' # '.' (Dead key) + b'\x00' # '/' (Dead key) + b'\xa7' # '0' + b'\x9e' # '1' + b'\x9f' # '2' + b'\xa0' # '3' + b'\xa1' # '4' + b'\xa2' # '5' + b'\xa3' # '6' + b'\xa4' # '7' + b'\xa5' # '8' + b'\xa6' # '9' + b'\x99' # ':' + b'\x8a' # ';' + b'\x1f' # '<' + b'\x2d' # '=' + b'\x20' # '>' + b'\x91' # '?' + b'\x23' # '@' + b'\x84' # 'A' + b'\x94' # 'B' + b'\x8b' # 'C' + b'\x8c' # 'D' + b'\x89' # 'E' + b'\xb8' # 'F' + b'\xb6' # 'G' + b'\xb7' # 'H' + b'\x87' # 'I' + b'\x93' # 'J' + b'\x85' # 'K' + b'\x92' # 'L' + b'\xb4' # 'M' + b'\xb3' # 'N' + b'\x95' # 'O' + b'\x88' # 'P' + b'\x90' # 'Q' + b'\x8f' # 'R' + b'\x8e' # 'S' + b'\x8d' # 'T' + b'\x96' # 'U' + b'\x98' # 'V' + b'\xb0' # 'W' + b'\x86' # 'X' + b'\x9b' # 'Y' + b'\xaf' # 'Z' + b'\x21' # '[' + b'\x1d' # '\\' + b'\x22' # ']' + b'\x23' # '^' + b'\x2c' # '_' + b'\x00' # '`' (Dead key) + b'\x04' # 'a' + b'\x14' # 'b' + b'\x0b' # 'c' + b'\x0c' # 'd' + b'\x09' # 'e' + b'\x38' # 'f' + b'\x36' # 'g' + b'\x37' # 'h' + b'\x07' # 'i' + b'\x13' # 'j' + b'\x05' # 'k' + b'\x12' # 'l' + b'\x34' # 'm' + b'\x33' # 'n' + b'\x15' # 'o' + b'\x08' # 'p' + b'\x10' # 'q' + b'\x0f' # 'r' + b'\x0e' # 's' + b'\x0d' # 't' + b'\x16' # 'u' + b'\x18' # 'v' + b'\x30' # 'w' + b'\x06' # 'x' + b'\x1b' # 'y' + b'\x2f' # 'z' + b'\x1b' # '{' + b'\x14' # '|' + b'\x06' # '}' + b'\x00' # '~' (Dead key) + b'\x00' + ) + NEED_ALTGR = "&'<>[\\]^_{|}¡±¿×æ÷ùœ–—…‰€−≠" + HIGHER_ASCII = { + 0x2014: 0x1e, # '—' + 0xab: 0x1f, # '«' + 0xbb: 0x20, # '»' + 0xb1: 0x24, # '±' + 0x2212: 0x25, # '−' + 0xf7: 0x26, # '÷' + 0xd7: 0x27, # '×' + 0xb0: 0xad, # '°' + 0x2260: 0x2d, # '≠' + 0x2030: 0x2e, # '‰' + 0xe9: 0x1a, # 'é' + 0xc9: 0x9a, # 'É' + 0x153: 0x15, # 'œ' + 0xe8: 0x17, # 'è' + 0xc8: 0x97, # 'È' + 0xa1: 0x1c, # '¡' + 0xe6: 0x04, # 'æ' + 0xf9: 0x16, # 'ù' + 0x20ac: 0x09, # '€' + 0x2013: 0x35, # '–' + 0xe7: 0x31, # 'ç' + 0xc7: 0xb1, # 'Ç' + 0xe0: 0x1d, # 'à' + 0xc0: 0x9d, # 'À' + 0x2026: 0x19, # '…' + 0x2019: 0x11, # '’' + 0xbf: 0x11, # '¿' + 0x202f: 0xac, # '\u202f' + 0xea: 0x64, # 'ê' + 0xca: 0xe4, # 'Ê' + } + COMBINED_KEYS = { + 0xc1: 0x1ac1, # 'Á' + 0x106: 0x1ac3, # 'Ć' + 0xc9: 0x1ac5, # 'É' + 0x1f4: 0x1ac7, # 'Ǵ' + 0xcd: 0x1ac9, # 'Í' + 0x1e30: 0x1acb, # 'Ḱ' + 0x139: 0x1acc, # 'Ĺ' + 0x1e3e: 0x1acd, # 'Ḿ' + 0x143: 0x1ace, # 'Ń' + 0xd3: 0x1acf, # 'Ó' + 0x1e54: 0x1ad0, # 'Ṕ' + 0x154: 0x1ad2, # 'Ŕ' + 0x15a: 0x1ad3, # 'Ś' + 0xda: 0x1ad5, # 'Ú' + 0x1d7: 0x1ad6, # 'Ǘ' + 0x1e82: 0x1ad7, # 'Ẃ' + 0xdd: 0x1ad9, # 'Ý' + 0x179: 0x1ada, # 'Ź' + 0x1fc: 0x1ac6, # 'Ǽ' + 0xe1: 0x1ae1, # 'á' + 0x107: 0x1ae3, # 'ć' + 0xe9: 0x1ae5, # 'é' + 0x1f5: 0x1ae7, # 'ǵ' + 0xed: 0x1ae9, # 'í' + 0x1e31: 0x1aeb, # 'ḱ' + 0x13a: 0x1aec, # 'ĺ' + 0x1e3f: 0x1aed, # 'ḿ' + 0x144: 0x1aee, # 'ń' + 0xf3: 0x1aef, # 'ó' + 0x1e55: 0x1af0, # 'ṕ' + 0x155: 0x1af2, # 'ŕ' + 0x15b: 0x1af3, # 'ś' + 0xfa: 0x1af5, # 'ú' + 0x1d8: 0x1af6, # 'ǘ' + 0x1e83: 0x1af7, # 'ẃ' + 0xfd: 0x1af9, # 'ý' + 0x17a: 0x1afa, # 'ź' + 0x1fd: 0x1ae6, # 'ǽ' + 0xb4: 0x1aa0, # '´' + 0xc0: 0x17c1, # 'À' + 0xc8: 0x17c5, # 'È' + 0xcc: 0x17c9, # 'Ì' + 0x1f8: 0x17ce, # 'Ǹ' + 0xd2: 0x17cf, # 'Ò' + 0xd9: 0x17d5, # 'Ù' + 0x1e80: 0x17d7, # 'Ẁ' + 0x1ef2: 0x17d9, # 'Ỳ' + 0xe0: 0x17e1, # 'à' + 0xe8: 0x17e5, # 'è' + 0xec: 0x17e9, # 'ì' + 0x1f9: 0x17ee, # 'ǹ' + 0xf2: 0x17ef, # 'ò' + 0xf9: 0x17f5, # 'ù' + 0x1e81: 0x17f7, # 'ẁ' + 0x1ef3: 0x17f9, # 'ỳ' + 0x60: 0x17a0, # '`' + 0xc2: 0x1c41, # 'Â' + 0x108: 0x1c43, # 'Ĉ' + 0xca: 0x1c45, # 'Ê' + 0x11c: 0x1c47, # 'Ĝ' + 0x124: 0x1c48, # 'Ĥ' + 0xce: 0x1c49, # 'Î' + 0x134: 0x1c4a, # 'Ĵ' + 0xd4: 0x1c4f, # 'Ô' + 0x15c: 0x1c53, # 'Ŝ' + 0xdb: 0x1c55, # 'Û' + 0x174: 0x1c57, # 'Ŵ' + 0x176: 0x1c59, # 'Ŷ' + 0x1e90: 0x1c5a, # 'Ẑ' + 0xe2: 0x1c61, # 'â' + 0x109: 0x1c63, # 'ĉ' + 0xea: 0x1c65, # 'ê' + 0x11d: 0x1c67, # 'ĝ' + 0x125: 0x1c68, # 'ĥ' + 0xee: 0x1c69, # 'î' + 0x135: 0x1c6a, # 'ĵ' + 0xf4: 0x1c6f, # 'ô' + 0x15d: 0x1c73, # 'ŝ' + 0xfb: 0x1c75, # 'û' + 0x175: 0x1c77, # 'ŵ' + 0x177: 0x1c79, # 'ŷ' + 0x1e91: 0x1c7a, # 'ẑ' + 0x5e: 0x1c20, # '^' + 0x1cd: 0x18c1, # 'Ǎ' + 0x10c: 0x18c3, # 'Č' + 0x10e: 0x18c4, # 'Ď' + 0x11a: 0x18c5, # 'Ě' + 0x1e6: 0x18c7, # 'Ǧ' + 0x21e: 0x18c8, # 'Ȟ' + 0x1cf: 0x18c9, # 'Ǐ' + 0x1e8: 0x18cb, # 'Ǩ' + 0x13d: 0x18cc, # 'Ľ' + 0x147: 0x18ce, # 'Ň' + 0x1d1: 0x18cf, # 'Ǒ' + 0x158: 0x18d2, # 'Ř' + 0x160: 0x18d3, # 'Š' + 0x164: 0x18d4, # 'Ť' + 0x1d3: 0x18d5, # 'Ǔ' + 0x17d: 0x18da, # 'Ž' + 0x1ee: 0x181b7, # 'Ǯ' + 0x1ce: 0x18e1, # 'ǎ' + 0x10d: 0x18e3, # 'č' + 0x10f: 0x18e4, # 'ď' + 0x11b: 0x18e5, # 'ě' + 0x1e7: 0x18e7, # 'ǧ' + 0x21f: 0x18e8, # 'ȟ' + 0x1d0: 0x18e9, # 'ǐ' + 0x1f0: 0x18ea, # 'ǰ' + 0x1e9: 0x18eb, # 'ǩ' + 0x13e: 0x18ec, # 'ľ' + 0x148: 0x18ee, # 'ň' + 0x1d2: 0x18ef, # 'ǒ' + 0x159: 0x18f2, # 'ř' + 0x161: 0x18f3, # 'š' + 0x165: 0x18f4, # 'ť' + 0x1d4: 0x18f5, # 'ǔ' + 0x17e: 0x18fa, # 'ž' + 0x1ef: 0x18292, # 'ǯ' + 0x2c7: 0x18a0, # 'ˇ' + 0x221e: 0x0ca0, # '∞' + 0x221a: 0x0cd6, # '√' + 0x2264: 0x0c2afd, # '≤' + 0x2265: 0x0c2afe, # '≥' + 0x23a: 0x12c1, # 'Ⱥ' + 0x23b: 0x12c3, # 'Ȼ' + 0x246: 0x12c5, # 'Ɇ' + 0x141: 0x12cc, # 'Ł' + 0xd8: 0x12cf, # 'Ø' + 0x23e: 0x12d4, # 'Ⱦ' + 0x23c: 0x12e3, # 'ȼ' + 0x247: 0x12e5, # 'ɇ' + 0x142: 0x12ec, # 'ł' + 0xf8: 0x12ef, # 'ø' + 0x1e9c: 0x121ff, # 'ẜ' + 0x2260: 0x12bd, # '≠' + 0x2244: 0x1222c3, # '≄' + 0x226e: 0x12bc, # '≮' + 0x226f: 0x12be, # '≯' + 0x2270: 0x122afd, # '≰' + 0x2271: 0x122afe, # '≱' + 0x2f: 0x12a0, # '/' + 0x243: 0x2fc2, # 'Ƀ' + 0x110: 0x2fc4, # 'Đ' + 0x1e4: 0x2fc7, # 'Ǥ' + 0x126: 0x2fc8, # 'Ħ' + 0x197: 0x2fc9, # 'Ɨ' + 0x248: 0x2fca, # 'Ɉ' + 0x23d: 0x2fcc, # 'Ƚ' + 0x24c: 0x2fd2, # 'Ɍ' + 0x166: 0x2fd4, # 'Ŧ' + 0x244: 0x2fd5, # 'Ʉ' + 0x24e: 0x2fd9, # 'Ɏ' + 0x1b5: 0x2fda, # 'Ƶ' + 0x180: 0x2fe2, # 'ƀ' + 0x111: 0x2fe4, # 'đ' + 0x1e5: 0x2fe7, # 'ǥ' + 0x127: 0x2fe8, # 'ħ' + 0x249: 0x2fea, # 'ɉ' + 0x19a: 0x2fec, # 'ƚ' + 0x24d: 0x2ff2, # 'ɍ' + 0x167: 0x2ff4, # 'ŧ' + 0x24f: 0x2ff9, # 'ɏ' + 0x1b6: 0x2ffa, # 'ƶ' + 0x2d: 0x2fa0, # '-' + 0xc4: 0x07c1, # 'Ä' + 0xcb: 0x07c5, # 'Ë' + 0x1e26: 0x07c8, # 'Ḧ' + 0xcf: 0x07c9, # 'Ï' + 0xd6: 0x07cf, # 'Ö' + 0xdc: 0x07d5, # 'Ü' + 0x1e84: 0x07d7, # 'Ẅ' + 0x1e8c: 0x07d8, # 'Ẍ' + 0x178: 0x07d9, # 'Ÿ' + 0xe4: 0x07e1, # 'ä' + 0xeb: 0x07e5, # 'ë' + 0x1e27: 0x07e8, # 'ḧ' + 0xef: 0x07e9, # 'ï' + 0xf6: 0x07ef, # 'ö' + 0x1e97: 0x07f4, # 'ẗ' + 0xfc: 0x07f5, # 'ü' + 0x1e85: 0x07f7, # 'ẅ' + 0x1e8d: 0x07f8, # 'ẍ' + 0xff: 0x07f9, # 'ÿ' + 0xa8: 0x07a0, # '¨' + 0xc7: 0x0bc3, # 'Ç' + 0x1e10: 0x0bc4, # 'Ḑ' + 0x228: 0x0bc5, # 'Ȩ' + 0x122: 0x0bc7, # 'Ģ' + 0x1e28: 0x0bc8, # 'Ḩ' + 0x136: 0x0bcb, # 'Ķ' + 0x13b: 0x0bcc, # 'Ļ' + 0x145: 0x0bce, # 'Ņ' + 0x156: 0x0bd2, # 'Ŗ' + 0x15e: 0x0bd3, # 'Ş' + 0x162: 0x0bd4, # 'Ţ' + 0xe7: 0x0be3, # 'ç' + 0x1e11: 0x0be4, # 'ḑ' + 0x229: 0x0be5, # 'ȩ' + 0x123: 0x0be7, # 'ģ' + 0x1e29: 0x0be8, # 'ḩ' + 0x137: 0x0beb, # 'ķ' + 0x13c: 0x0bec, # 'ļ' + 0x146: 0x0bee, # 'ņ' + 0x157: 0x0bf2, # 'ŗ' + 0x15f: 0x0bf3, # 'ş' + 0x163: 0x0bf4, # 'ţ' + 0xb8: 0x0ba0, # '¸' + 0x2070: 0x0db0, # '⁰' + 0xb9: 0x0db1, # '¹' + 0xb2: 0x0db2, # '²' + 0xb3: 0x0db3, # '³' + 0x2074: 0x0db4, # '⁴' + 0x2075: 0x0db5, # '⁵' + 0x2076: 0x0db6, # '⁶' + 0x2077: 0x0db7, # '⁷' + 0x2078: 0x0db8, # '⁸' + 0x2079: 0x0db9, # '⁹' + 0x207d: 0x0da8, # '⁽' + 0x207e: 0x0da9, # '⁾' + 0x207a: 0x0dab, # '⁺' + 0x207b: 0x0dad, # '⁻' + 0x207c: 0x0dbd, # '⁼' + 0x1d49: 0x0da0, # 'ᵉ' + 0x2039: 0x0eab, # '‹' + 0x203a: 0x0ebb, # '›' + 0xad: 0x0ead, # '\xad' + 0xf0: 0x0ee4, # 'ð' + 0xd0: 0x0ec4, # 'Ð' + 0x133: 0x0eea, # 'ij' + 0x132: 0x0eca, # 'IJ' + 0x259: 0x0ee5, # 'ə' + 0x18f: 0x0ec5, # 'Ə' + 0x3b8: 0x36f5, # 'θ' + 0x3f4: 0x0ec3, # 'ϴ' + 0xfe: 0x0ef4, # 'þ' + 0xde: 0x0ed4, # 'Þ' + 0xdf: 0x0ea0, # 'ß' + 0x1e9e: 0x0e1ff, # 'ẞ' + 0x14b: 0x0eee, # 'ŋ' + 0x14a: 0x0ece, # 'Ŋ' + 0xba: 0x0eaf, # 'º' + 0x2bb: 0x0e2098, # 'ʻ' + 0x2bc: 0x0e2099, # 'ʼ' + 0xaa: 0x0e2db, # 'ª' + 0x2003: 0x0e20af, # '\u2003' + 0x102: 0x0fc1, # 'Ă' + 0x114: 0x0fc5, # 'Ĕ' + 0x11e: 0x0fc7, # 'Ğ' + 0x12c: 0x0fc9, # 'Ĭ' + 0x14e: 0x0fcf, # 'Ŏ' + 0x16c: 0x0fd5, # 'Ŭ' + 0x103: 0x0fe1, # 'ă' + 0x115: 0x0fe5, # 'ĕ' + 0x11f: 0x0fe7, # 'ğ' + 0x12d: 0x0fe9, # 'ĭ' + 0x14f: 0x0fef, # 'ŏ' + 0x16d: 0x0ff5, # 'ŭ' + 0x2d8: 0x0fa0, # '˘' + 0xc3: 0x33c1, # 'Ã' + 0x1ebc: 0x33c5, # 'Ẽ' + 0x128: 0x33c9, # 'Ĩ' + 0xd1: 0x33ce, # 'Ñ' + 0xd5: 0x33cf, # 'Õ' + 0x168: 0x33d5, # 'Ũ' + 0x1e7c: 0x33d6, # 'Ṽ' + 0x1ef8: 0x33d9, # 'Ỹ' + 0xe3: 0x33e1, # 'ã' + 0x1ebd: 0x33e5, # 'ẽ' + 0x129: 0x33e9, # 'ĩ' + 0xf1: 0x33ee, # 'ñ' + 0xf5: 0x33ef, # 'õ' + 0x169: 0x33f5, # 'ũ' + 0x1e7d: 0x33f6, # 'ṽ' + 0x1ef9: 0x33f9, # 'ỹ' + 0x2243: 0x33ad, # '≃' + 0x2248: 0x33bd, # '≈' + 0x2272: 0x33bc, # '≲' + 0x2273: 0x33be, # '≳' + 0x7e: 0x33a0, # '~' + 0x100: 0x34c1, # 'Ā' + 0x112: 0x34c5, # 'Ē' + 0x1e20: 0x34c7, # 'Ḡ' + 0x12a: 0x34c9, # 'Ī' + 0x14c: 0x34cf, # 'Ō' + 0x16a: 0x34d5, # 'Ū' + 0x232: 0x34d9, # 'Ȳ' + 0x101: 0x34e1, # 'ā' + 0x113: 0x34e5, # 'ē' + 0x1e21: 0x34e7, # 'ḡ' + 0x12b: 0x34e9, # 'ī' + 0x14d: 0x34ef, # 'ō' + 0x16b: 0x34f5, # 'ū' + 0x233: 0x34f9, # 'ȳ' + 0x1e2: 0x34c6, # 'Ǣ' + 0x1e3: 0x34e6, # 'ǣ' + 0xaf: 0x34a0, # '¯' + 0xc5: 0x10c1, # 'Å' + 0x16e: 0x10d5, # 'Ů' + 0xe5: 0x10e1, # 'å' + 0x16f: 0x10f5, # 'ů' + 0x1e98: 0x10f7, # 'ẘ' + 0x1e99: 0x10f9, # 'ẙ' + 0x2da: 0x10a0, # '˚' + 0x391: 0x36c1, # 'Α' + 0x392: 0x36c2, # 'Β' + 0x3a8: 0x36c3, # 'Ψ' + 0x394: 0x36c4, # 'Δ' + 0x395: 0x36c5, # 'Ε' + 0x3a6: 0x36c6, # 'Φ' + 0x393: 0x36c7, # 'Γ' + 0x397: 0x36c8, # 'Η' + 0x399: 0x36c9, # 'Ι' + 0x39e: 0x36ca, # 'Ξ' + 0x39a: 0x36cb, # 'Κ' + 0x39b: 0x36cc, # 'Λ' + 0x39c: 0x36cd, # 'Μ' + 0x39d: 0x36ce, # 'Ν' + 0x39f: 0x36cf, # 'Ο' + 0x3a0: 0x36d0, # 'Π' + 0x3a1: 0x36d2, # 'Ρ' + 0x3a3: 0x36d3, # 'Σ' + 0x3a4: 0x36d4, # 'Τ' + 0x398: 0x36d5, # 'Θ' + 0x3a9: 0x36d6, # 'Ω' + 0x3a7: 0x36d8, # 'Χ' + 0x3a5: 0x36d9, # 'Υ' + 0x396: 0x36da, # 'Ζ' + 0x3b1: 0x36e1, # 'α' + 0x3b2: 0x36e2, # 'β' + 0x3c8: 0x36e3, # 'ψ' + 0x3b4: 0x36e4, # 'δ' + 0x3b5: 0x36e5, # 'ε' + 0x3c6: 0x36e6, # 'φ' + 0x3b3: 0x36e7, # 'γ' + 0x3b7: 0x36e8, # 'η' + 0x3b9: 0x36e9, # 'ι' + 0x3be: 0x36ea, # 'ξ' + 0x3ba: 0x36eb, # 'κ' + 0x3bb: 0x36ec, # 'λ' + 0x3bc: 0x36ed, # 'μ' + 0x3bd: 0x36ee, # 'ν' + 0x3bf: 0x36ef, # 'ο' + 0x3c0: 0x36f0, # 'π' + 0x3c1: 0x36f2, # 'ρ' + 0x3c3: 0x36f3, # 'σ' + 0x3c4: 0x36f4, # 'τ' + 0x3c9: 0x36f6, # 'ω' + 0x3c2: 0x36f7, # 'ς' + 0x3c7: 0x36f8, # 'χ' + 0x3c5: 0x36f9, # 'υ' + 0x3b6: 0x36fa, # 'ζ' + 0xb5: 0x36a0, # 'µ' + 0x1ea0: 0x37c1, # 'Ạ' + 0x1e04: 0x37c2, # 'Ḅ' + 0x1e0c: 0x37c4, # 'Ḍ' + 0x1eb8: 0x37c5, # 'Ẹ' + 0x1e24: 0x37c8, # 'Ḥ' + 0x1eca: 0x37c9, # 'Ị' + 0x1e32: 0x37cb, # 'Ḳ' + 0x1e36: 0x37cc, # 'Ḷ' + 0x1e42: 0x37cd, # 'Ṃ' + 0x1e46: 0x37ce, # 'Ṇ' + 0x1ecc: 0x37cf, # 'Ọ' + 0x1e5a: 0x37d2, # 'Ṛ' + 0x1e62: 0x37d3, # 'Ṣ' + 0x1e6c: 0x37d4, # 'Ṭ' + 0x1ee4: 0x37d5, # 'Ụ' + 0x1e7e: 0x37d6, # 'Ṿ' + 0x1e88: 0x37d7, # 'Ẉ' + 0x1ef4: 0x37d9, # 'Ỵ' + 0x1e92: 0x37da, # 'Ẓ' + 0x1ea1: 0x37e1, # 'ạ' + 0x1e05: 0x37e2, # 'ḅ' + 0x1e0d: 0x37e4, # 'ḍ' + 0x1eb9: 0x37e5, # 'ẹ' + 0x1e25: 0x37e8, # 'ḥ' + 0x1ecb: 0x37e9, # 'ị' + 0x1e33: 0x37eb, # 'ḳ' + 0x1e37: 0x37ec, # 'ḷ' + 0x1e43: 0x37ed, # 'ṃ' + 0x1e47: 0x37ee, # 'ṇ' + 0x1ecd: 0x37ef, # 'ọ' + 0x1e5b: 0x37f2, # 'ṛ' + 0x1e63: 0x37f3, # 'ṣ' + 0x1e6d: 0x37f4, # 'ṭ' + 0x1ee5: 0x37f5, # 'ụ' + 0x1e7f: 0x37f6, # 'ṿ' + 0x1e89: 0x37f7, # 'ẉ' + 0x1ef5: 0x37f9, # 'ỵ' + 0x1e93: 0x37fa, # 'ẓ' + 0x2e: 0x37a0, # '.' + 0x104: 0x38c1, # 'Ą' + 0x118: 0x38c5, # 'Ę' + 0x12e: 0x38c9, # 'Į' + 0x1ea: 0x38cf, # 'Ǫ' + 0x172: 0x38d5, # 'Ų' + 0x105: 0x38e1, # 'ą' + 0x119: 0x38e5, # 'ę' + 0x12f: 0x38e9, # 'į' + 0x1eb: 0x38ef, # 'ǫ' + 0x173: 0x38f5, # 'ų' + 0x2db: 0x38a0, # '˛' + } diff --git a/src/keycode_win_frnb.py b/src/keycode_win_frnb.py new file mode 100644 index 0000000..2cab58a --- /dev/null +++ b/src/keycode_win_frnb.py @@ -0,0 +1,165 @@ +# SPDX-FileCopyrightText: 2022 Neradoc NeraOnGit@ri1.fr +# SPDX-License-Identifier: MIT +""" +This file was automatically generated using Circuitpython_Keyboard_Layouts +""" + + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/Neradoc/Circuitpython_Keyboard_Layouts.git" + + +class Keycode: + A = 0x04 + B = 0x14 + C = 0x0b + D = 0x0c + E = 0x09 + F = 0x38 + G = 0x36 + H = 0x37 + I = 0x07 + J = 0x13 + K = 0x05 + L = 0x12 + M = 0x34 + N = 0x33 + O = 0x15 + P = 0x08 + Q = 0x10 + R = 0x0f + S = 0x0e + T = 0x0d + U = 0x16 + V = 0x18 + W = 0x30 + X = 0x06 + Y = 0x1b + Z = 0x2f + ALT = 0xe2 + END = 0x4d + F1 = 0x3a + F2 = 0x3b + F3 = 0x3c + F4 = 0x3d + F5 = 0x3e + F6 = 0x3f + F7 = 0x40 + F8 = 0x41 + F9 = 0x42 + F10 = 0x43 + F11 = 0x44 + F12 = 0x45 + F13 = 0x68 + F14 = 0x69 + F15 = 0x6a + F16 = 0x6b + F17 = 0x6c + F18 = 0x6d + F19 = 0x6e + F20 = 0x6f + F21 = 0x70 + F22 = 0x71 + F23 = 0x72 + F24 = 0x73 + GUI = 0xe3 + ONE = 0x1e + SIX = 0x23 + TAB = 0x2b + TWO = 0x1f + FIVE = 0x22 + FOUR = 0x21 + HOME = 0x4a + NINE = 0x26 + ZERO = 0x27 + ALTGR = 0xe6 + Brève = 0x0f + COMMA = 0x0a + Caron = 0x18 + EIGHT = 0x25 + ENTER = 0x28 + MINUS = 0x2d + PAUSE = 0x48 + QUOTE = 0x35 + SEVEN = 0x24 + SHIFT = 0xe1 + SPACE = 0x2c + THREE = 0x20 + Tilde = 0x33 + Tréma = 0x07 + APPLICATION = 0x65 + Accent_aigu = 0x1a + Accent_circonflexe = 0x1c + Accent_grave = 0x17 + BACKSLASH = 0x31 + BACKSPACE = 0x2a + Barre_couvrante = 0x2f + CAPS_LOCK = 0x39 + COMMAND = 0xe3 + CONTROL = 0xe0 + Cédille = 0x0b + DELETE = 0x4c + DOWN_ARROW = 0x51 + EQUALS = 0x2e + ESCAPE = 0x29 + Exposants = 0x0d + FORWARD_SLASH = 0x17 + GRAVE_ACCENT = 0x11 + INSERT = 0x49 + KEYPAD_ASTERISK = 0x55 + KEYPAD_EIGHT = 0x60 + KEYPAD_FIVE = 0x5d + KEYPAD_FORWARD_SLASH = 0x54 + KEYPAD_FOUR = 0x5c + KEYPAD_MINUS = 0x56 + KEYPAD_NINE = 0x61 + KEYPAD_NUMLOCK = 0x53 + KEYPAD_ONE = 0x59 + KEYPAD_PERIOD = 0x63 + KEYPAD_PLUS = 0x57 + KEYPAD_SEVEN = 0x5f + KEYPAD_SIX = 0x5e + KEYPAD_THREE = 0x5b + KEYPAD_TWO = 0x5a + KEYPAD_ZERO = 0x62 + LEFT_ALT = 0xe2 + LEFT_ARROW = 0x50 + LEFT_BRACKET = 0x1c + LEFT_CONTROL = 0xe0 + LEFT_GUI = 0xe3 + LEFT_SHIFT = 0xe1 + Latin_et_ponctuation = 0x0e + Lettres_grecques = 0x36 + Macron = 0x34 + OEM_102 = 0x64 + OPTION = 0xe2 + Ogonek = 0x38 + PAGE_DOWN = 0x4e + PAGE_UP = 0x4b + PERIOD = 0x19 + PRINT_SCREEN = 0x46 + Point_souscrit = 0x37 + RETURN = 0x28 + RIGHT_ALT = 0xe6 + RIGHT_ARROW = 0x4f + RIGHT_BRACKET = 0x1d + RIGHT_CONTROL = 0xe4 + RIGHT_GUI = 0xe7 + RIGHT_SHIFT = 0xe5 + Rond_en_chef = 0x10 + SCROLL_LOCK = 0x47 + SEMICOLON = 0x1a + SPACEBAR = 0x2c + UP_ARROW = 0x52 + WINDOWS = 0xe3 + Symboles_scientifiques = 0x0c + Barre_oblique_couvrante = 0x12 + + @classmethod + def modifier_bit(cls, keycode): + """Return the modifer bit to be set in an HID keycode report if this is a + modifier key; otherwise return 0.""" + return ( + 1 << (keycode - 0xE0) if cls.LEFT_CONTROL <= keycode <= cls.RIGHT_GUI else 0 + ) + \ No newline at end of file diff --git a/src/lib/adafruit_bitmap_font/__init__.py b/src/lib/adafruit_bitmap_font/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/adafruit_display_shapes/__init__.py b/src/lib/adafruit_display_shapes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/macros/1-ff.py b/src/macros/1-ff.py new file mode 100644 index 0000000..f1d26ce --- /dev/null +++ b/src/macros/1-ff.py @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from keycode_win_frnb import Keycode # Use the french bepo layout +from adafruit_hid.mouse import Mouse + +import skeleton +from actions import Action + +def reload(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.R]) + +def tab(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.T]) + +def close(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.W]) + +def page_up(macropad, key, pressed): + macropad.mouse.move( + 0, + 0, + 1) + +def page_down(macropad, key, pressed): + macropad.mouse.move( + 0, + 0, + -1) + +class App(object): + def __init__(self): + self.group = skeleton.Group(0x050500, + [(9, "Page") + ,(10, "Tab")] + ) + + def rot_cw(self, macropad, key, pressed): + if self.group.selected == 10: + Action().key(pressed, [Keycode.CONTROL, Keycode.PAGE_DOWN]) + else: + page_down(macropad, key, pressed) + + def rot_ccw(self, macropad, key, pressed): + if self.group.selected == 10: + Action().key(pressed, [Keycode.CONTROL, Keycode.PAGE_UP]) + else: + page_up(macropad, key, pressed) + +def key(code): + def action(macropad, key, pressed): + Action().key(pressed, code) + return action + +def build_application(): + app = App() + + configuration = skeleton.Configuration("Firefox") + configuration.registerKey(0, "^r", reload, 0x000500) + configuration.registerKey(1, "^t", tab, 0x000005) + configuration.registerKey(2, "x", close, 0x050000) + configuration.registerKey(3, "<", key([Keycode.CONTROL, Keycode.PAGE_UP]), 0x050505) + configuration.registerKey(5, ">", key([Keycode.CONTROL, Keycode.PAGE_DOWN]), 0x050505) + configuration.registerKey(12, "", app.rot_cw, None) + configuration.registerKey(13, "", app.rot_ccw,None) + configuration.registerGroup(app.group) + return configuration + +configuration = build_application() diff --git a/src/macros/2-num.py b/src/macros/2-num.py new file mode 100644 index 0000000..68b0911 --- /dev/null +++ b/src/macros/2-num.py @@ -0,0 +1,103 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values +import colorsys + +import skeleton +from actions import Action + + +class Color(object): + """ Represent the color of a key in the HSV model. + + The class implements the int() function in order to being compatible with + the remaining of the application. + """ + def __init__(self, h, s, v): + self.col = (h, s, v) + + def __int__(self): + r, g, b = colorsys.hsv_to_rgb(self.col[0], self.col[1], self.col[2]) + return (r<<16) + (g<<8) + b + +def build_application(): + configuration = skeleton.Configuration("Num pad") + configuration.registerKey( + 0, + "7", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_SEVEN]), + Color(0.333, 1.0, 0.0196)) + configuration.registerKey( + 1, + "8", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_EIGHT]), + Color(0.266, 1.0, 0.0196)) + configuration.registerKey( + 2, + "9", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_NINE]), + Color(0.166, 1.0, 0.0196)) + configuration.registerKey( + 3, + "4", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_FOUR]), + Color(0.4, 1.0, 0.0196)) + configuration.registerKey( + 4, + "5", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_FIVE]), + Color(0.333, 0.6, 0.0196)) + configuration.registerKey( + 5, + "6", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_SIX]), + Color(0.16, 0.6, 0.0196)) + configuration.registerKey( + 6, + "1", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_ONE]), + # 0x000505 + Color(0.5, 1.0, 0.0196)) + configuration.registerKey( + 7, + "2", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_TWO]), + Color(0.5, 0.6, 0.0196)) + configuration.registerKey( + 8, + "3", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_THREE]), + # 0x050505 + Color(0.01, 0.01, 0.0196)) + configuration.registerKey( + 9, + "0", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_ZERO]), + # 0x000507 + Color(0.54, 1.0, 0.027)) + + # The dot key. + configuration.registerKey( + 11, + ".", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_PERIOD]), + # 0x050507 + Color(0.66, 0.286, 0.027)) + return configuration + +configuration = build_application() diff --git a/src/macros/3-fkeys.py b/src/macros/3-fkeys.py new file mode 100644 index 0000000..c35c500 --- /dev/null +++ b/src/macros/3-fkeys.py @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values + +import skeleton +from actions import Action + +def build_application(): + configuration = skeleton.Configuration("F-Keys") + configuration.registerKey( + 0, + "F13", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F13]), + None) + configuration.registerKey( + 1, + "F14", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F14]), + None) + configuration.registerKey( + 2, + "F15", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F15]), + None) + configuration.registerKey( + 3, + "F16", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F16]), + None) + configuration.registerKey( + 4, + "F17", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F17]), + None) + configuration.registerKey( + 5, + "F18", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F18]), + None) + configuration.registerKey( + 6, + "F19", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F19]), + None) + configuration.registerKey( + 7, + "F20", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F20]), + None) + configuration.registerKey( + 8, + "F21", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F21]), + None) + configuration.registerKey( + 9, + "F22", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F22]), + None) + configuration.registerKey( + 10, + "F23", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F23]), + None) + configuration.registerKey( + 11, + "F24", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.F24]), + None) + return configuration + +configuration = build_application() diff --git a/src/macros/4-move_mouse.py b/src/macros/4-move_mouse.py new file mode 100644 index 0000000..ef6ede0 --- /dev/null +++ b/src/macros/4-move_mouse.py @@ -0,0 +1,54 @@ +import skeleton + +class Color(object): + """ Return a color which may change over time. + """ + def __init__(self, code): + self.code = code + + def __int__(self): + return self.code + +class NoIdle(object): + + def __init__(self): + self.x = 10 + self.color = Color(0x000500) + self.active = False + + def update(self, macropad): + if not self.active: + return + + self.x *= -1 + macropad.mouse.move( + self.x, + 0, + 0) + # Update the color in red or blue in order to show that the heartbeat + # is active. + self.color.code = 0x010000 if self.x > 0 else 0x000001 + macropad.pixels[4] = int(self.color) + macropad.pixels.show() + + def pause(self, macropad, key, pressed): + if pressed: + self.active = not self.active + if self.active: + self.update(macropad) + else: + # Restore the default color (green) + self.color.code = 0x000500 + macropad.pixels[4] = int(self.color) + macropad.pixels.show() + + +def build_application(): + configuration = skeleton.Configuration("NoIdle") + actor = NoIdle() + configuration.setTick(5000, actor.update) + configuration.registerKey(4, "switch", actor.pause, actor.color) + return configuration + +configuration = build_application() + diff --git a/src/macros/5-irssi.py b/src/macros/5-irssi.py new file mode 100644 index 0000000..bcf5d81 --- /dev/null +++ b/src/macros/5-irssi.py @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from keycode_win_frnb import Keycode # Use the french bepo layout + +import skeleton +from actions import Action + +def wc(macropad, key, pressed): + if not pressed: + return + Action().sequence( + [ [Keycode.KEYPAD_FORWARD_SLASH] + , [Keycode.W] + , [Keycode.C] + , [Keycode.ENTER] + ] + ) + +def next_one(macropad, key, pressed): + Action().key(pressed, [Keycode.ALT, Keycode.A]) + +def build_application(): + + configuration = skeleton.Configuration("Irssi") + configuration.visible = True + configuration.registerKey(0, "next.", next_one, 0x000200) + configuration.registerKey(2, "/wc", wc, 0x020000) + + + return configuration + +configuration = build_application() + + diff --git a/src/macros/99-none.py b/src/macros/99-none.py new file mode 100644 index 0000000..3c15de4 --- /dev/null +++ b/src/macros/99-none.py @@ -0,0 +1,3 @@ +import skeleton + +configuration = skeleton.Configuration("Nothing") diff --git a/src/macros/99-wm.py b/src/macros/99-wm.py new file mode 100644 index 0000000..225044b --- /dev/null +++ b/src/macros/99-wm.py @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from adafruit_hid.keycode import Keycode # REQUIRED if using Keycode.* values +from adafruit_hid.mouse import Mouse + +import skeleton +from actions import Action + +def desktop4(macropad, key, pressed): + Action().key(pressed, [Keycode.RIGHT_ALT, Keycode.F4]) + +def desktop3(macropad, key, pressed): + Action().key(pressed, [Keycode.RIGHT_ALT, Keycode.F3]) + +def desktop2(macropad, key, pressed): + Action().key(pressed, [Keycode.RIGHT_ALT, Keycode.F2]) + +def desktop1(macropad, key, pressed): + Action().key(pressed, [Keycode.RIGHT_ALT, Keycode.F1]) + +def rot_cw(macropad, key, pressed): + Action().key(pressed, [Keycode.GUI]) + if pressed: + macropad.mouse.move( + 0, + 0, + -1) + +def rot_acw(macropad, key, pressed): + Action().key(pressed, [Keycode.GUI]) + if pressed: + macropad.mouse.move( + 0, + 0, + 1) + +def build_application(): + configuration = skeleton.Configuration("WM") + configuration.registerKey(2, "F4", desktop4, None) + configuration.registerKey(5, "F3", desktop3, None) + configuration.registerKey(8, "F2", desktop2, None) + configuration.registerKey(11, "F1", desktop1, None) + configuration.registerKey(12, "", rot_cw, None) + configuration.registerKey(13, "", rot_acw, None) + return configuration + +configuration = build_application() diff --git a/src/macros/olympia.py b/src/macros/olympia.py new file mode 100644 index 0000000..b124075 --- /dev/null +++ b/src/macros/olympia.py @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from keycode_win_frnb import Keycode # Use the french bepo layout +import colorsys + +import skeleton +from actions import Action + + +class Color(object): + """ Represent the color of a key in the HSV model. + + The class implements the int() function in order to being compatible with + the remaining of the application. + """ + def __init__(self, h, s, v): + self.col = (h, s, v) + + def __int__(self): + r, g, b = colorsys.hsv_to_rgb(self.col[0], self.col[1], self.col[2]) + return (r<<16) + (g<<8) + b + +def key(code): + def action(macropad, key, pressed): + Action().key(pressed, code) + return action + +def build_application(): + configuration = skeleton.Configuration("Olympia") + configuration.hidden = False + configuration.registerKey(0, "vue", key([Keycode.V]), 0x000500) + configuration.registerKey(1, "map", key([Keycode.M]), 0x000005) + configuration.registerKey(2, "x", key([Keycode.CONTROL, Keycode.W]), 0x050000) + configuration.registerKey(3, "\\", key([Keycode.KEYPAD_SEVEN]), Color(0.333, 1.0, 0.0196)) + configuration.registerKey( + 4, + "|", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_EIGHT]), + Color(0.266, 1.0, 0.0196)) + configuration.registerKey( + 5, + "/", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_NINE]), + Color(0.166, 1.0, 0.0196)) + configuration.registerKey( + 6, + "<", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_FOUR]), + Color(0.4, 1.0, 0.0196)) + + configuration.registerKey(7, "Inventaire", key([Keycode.I]), Color(0.333, 0.6, 0.0196)) + + configuration.registerKey( + 8, + ">", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_SIX]), + # 0x050502 + Color(0.16, 0.6, 0.0196)) + configuration.registerKey( + 9, + "/", + key([Keycode.KEYPAD_ONE]), + 0x000505) + configuration.registerKey( + 10, + "|", + lambda macropad, key, pressed: Action().key(pressed, [Keycode.KEYPAD_TWO]), + Color(0.5, 0.6, 0.0196)) + configuration.registerKey( + 11, + "\\", + key([Keycode.KEYPAD_THREE]), + 0x050505) + return configuration + +configuration = build_application() diff --git a/src/macros/santana.py b/src/macros/santana.py new file mode 100644 index 0000000..2fd88c3 --- /dev/null +++ b/src/macros/santana.py @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from keycode_win_frnb import Keycode # Use the french bepo layout +from adafruit_hid.mouse import Mouse + +import skeleton +from actions import Action + +def groovy(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.G]) + +def log(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.L]) + +def dql(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.SHIFT, Keycode.F]) + +def package(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.P]) + +def build_application(): + + configuration = skeleton.Configuration("Santana") + configuration.visible = False + configuration.registerKey(0, "groovy", groovy, 0x000500) + configuration.registerKey(1, "logs", log, 0x000005) + + configuration.registerKey(3, "dql", dql, 0x050500) + configuration.registerKey(4, "package",package, 0x050000) + return configuration + +configuration = build_application() diff --git a/src/macros/teams.py b/src/macros/teams.py new file mode 100644 index 0000000..621f8b6 --- /dev/null +++ b/src/macros/teams.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from keycode_win_frnb import Keycode # Use the french bepo layout + +import skeleton +from actions import Action + +def conv(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.TWO]) + +def planning(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.FOUR]) + +def link(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.K]) + +def build_application(): + + configuration = skeleton.Configuration("Teams") + configuration.visible = False + configuration.registerKey(0, "Convers.", conv, 0x050000) + configuration.registerKey(3, "Planning", planning, 0x050200) + configuration.registerKey(2, "Link", link, 0x020002) + + + return configuration + +configuration = build_application() + diff --git a/src/macros/zim.py b/src/macros/zim.py new file mode 100644 index 0000000..fa93c4a --- /dev/null +++ b/src/macros/zim.py @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: 2021 Phillip Burgess for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# MACROPAD Hotkeys example: Mouse control + +# The syntax for Mouse macros is highly peculiar, in order to maintain +# backward compatibility with the original keycode-only macro files. +# The third item for each macro is a list in brackets, and each value within +# is normally an integer (Keycode), float (delay) or string (typed literally). +# Consumer Control codes were added as list-within-list, and then mouse +# further complicates this by adding dicts-within-list. Each mouse-related +# dict can have any mix of keys 'buttons' w/integer mask of button values +# (positive to press, negative to release), 'x' w/horizontal motion, +# 'y' w/vertical and 'wheel' with scrollwheel motion. + +# To reference Mouse constants, import Mouse like so... +from keycode_win_frnb import Keycode # Use the french bepo layout + +import skeleton +from actions import Action + +def t1(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.SHIFT, Keycode.ONE]) + +def t2(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.SHIFT, Keycode.TWO]) + +def t3(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.SHIFT, Keycode.THREE]) + +def none(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.SHIFT, Keycode.NINE]) + +def link(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.L]) + +def build_application(): + + configuration = skeleton.Configuration("Zim") + configuration.visible = False + configuration.registerKey(0, "T1", t1, 0x000500) + configuration.registerKey(3, "T2", t2, 0x000200) + configuration.registerKey(6, "T3", t3, 0x000100) + configuration.registerKey(9, "None", none, 0x000000) + + configuration.registerKey(2, "Link", link, 0x020002) + + + return configuration + +configuration = build_application() + diff --git a/src/menu.py b/src/menu.py new file mode 100644 index 0000000..319cae1 --- /dev/null +++ b/src/menu.py @@ -0,0 +1,23 @@ +import skeleton + +def build_application(apps): + + enabled_apps = [app for app in apps if app.visible] + max_l = min(12, len(enabled_apps)) + configuration = skeleton.Configuration("Application menu") + for index in range(max_l): + if enabled_apps[index] is None: + break + + # I use a lambda function here, the index is stored in a closure + # otherwise the value is erased and only keep the last iteration + # index + callback = lambda n:lambda macropad, key, pressed:enabled_apps[n] + + configuration.registerKey( + index, + enabled_apps[index].title, + callback(index), + None + ) + return configuration diff --git a/src/readme.rst b/src/readme.rst new file mode 100644 index 0000000..721bb53 --- /dev/null +++ b/src/readme.rst @@ -0,0 +1,73 @@ +.. -*- mode: rst -*- +.. -*- coding: utf-8 -*- + +Application macropad + +Utilisation +=========== + +Activer le système de fichier +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Lors du démarrage, le système de fichier n’est pas monté. C’est à dire que +le clavier n’apparait pas comme un lecteur auprès du système. + +Pour activer cette fonctionnalité, il faut appuyer sur la touche située en bas +à droite du clavier pendant le démarrage du clavier. + +:: + + . . . + . . . + . . . + . . X + +Ce code est géré dans le fichier "boot.py" + +Pilotage par port série +~~~~~~~~~~~~~~~~~~~~~~~ + +L’application peut recevoir des commandes sur le port série : l’envoi d’un json +tel que : + +.. code:: json + + [{"layout": "valeur à charger"}] + +va changer le programme actif sur le macropad pour celui qui a été donné en +paramètre. + +De la meme manière, le programme envoie sur le port-série les changements qui +sont provoqués par l’utilisateur. Ainsi, il est possible d’avoir une +application sur le PC qui se synchronise avec le Macropad sur la disposition à +charger. + + +Code +==== + +Configuration clavier +~~~~~~~~~~~~~~~~~~~~~ + +Le clavier ne connait pas les codes correspondants aux touches saisies. +L’application n’est capable que d’emettre les codes des touches, mais cela +n’aide pas la construction d’une application lisible. + +Les codes BÉPO sont disponibles dans le fichier `keycode_win_frnb.py` qui fait +l’association des différentes touches : + +.. code:: python + + from keycode_win_frnb import Keycode # Use the french bepo layout + + def reload(macropad, key, pressed): + Action().key(pressed, [Keycode.CONTROL, Keycode.R]) + +Compilation +=========== + +Les fichiers mpy sont des fichiers compilés correspondants aux fichier py mais +occupants moins de place sur le disque. Ils doivent etre compilés à l’aide de +l’outil mpy-cross qui est téléchargeable ici : + +https://adafruit-circuit-python.s3.amazonaws.com/index.html?prefix=bin/mpy-cross/ diff --git a/src/skeleton.py b/src/skeleton.py new file mode 100644 index 0000000..8497d34 --- /dev/null +++ b/src/skeleton.py @@ -0,0 +1,199 @@ +import displayio +import terminalio +from adafruit_display_shapes.rect import Rect +from adafruit_display_text import label + +from collections import namedtuple +from supervisor import ticks_ms +from actions import Action +import ticks + +class Group(object): + def __init__(self, color, actions): + + # The color of the LED for the selected element + self.color = color + + self.actions = actions + + self._selected = actions[0][0] + + @property + def selected(self): + return self._selected + + @selected.setter + def selected(self, value): + """Set the value, and switch the LED accordingly""" + + keys = [action[0] for action in self.actions] + + if value not in keys: + return + + for keyId in keys: + Action().macropad.pixels[keyId] = 0 + Action().macropad.pixels[value] = int(self.color) + self._selected = value + +class Configuration(object): + + def __init__(self, title): + + # Set the application available in the menu by default + self.visible = True + self.Action = namedtuple("Action", ("name", "callback", "color")) + self.title = title + self.keys = { + 0 : None, 1 : None, 2 : None, + 3 : None, 4 : None, 5 : None, + 6 : None, 7 : None, 8 : None, + 9 : None, 10 : None, 11 : None, + 12 : None, + 13 : None, + } + self.group = [] + self.tick = None + + def registerKey(self, keyId, name, callback, color): + if keyId > 13: + return + self.keys[keyId] = self.Action(name, callback, color) + + def registerGroup(self, group): + self.group.append(group) + + def setTick(self, duration, callback): + self.tick = (duration, callback) + +class Handler(object): + + def __init__(self, macropad, configuration): + self.macropad = macropad + self.group = displayio.Group() + + self.keys = { + 0 : None, 1 : None, 2 : None, + 3 : None, 4 : None, 5 : None, + 6 : None, 7 : None, 8 : None, + 9 : None, 10 : None, 11 : None, + 12 : None, + 13 : None, + } + + self.colors = { + 0 : None, 1 : None, 2 : None, + 3 : None, 4 : None, 5 : None, + 6 : None, 7 : None, 8 : None, + 9 : None, 10 : None, 11 : None, + } + + self.options = configuration.group + + self.name = configuration.title + + for index, value in configuration.keys.items(): + if value is None: + continue + self.keys[index] = value.name, value.callback + self.colors[index] = value.color + + for option in self.options: + for action in option.actions: + keyId = action[0] + self.keys[keyId] = action[1], lambda macropad, key, pressed: None + self.macropad.pixels[option.selected] = int(option.color) + + self.periodic_callback = None + self.next_tick = None + + if configuration.tick is not None: + self.periodic_callback = configuration.tick + self._update_tick() + self.macropad.pixels.show() + + def _update_tick(self): + self.next_tick = ticks.add( + ticks_ms(), + self.periodic_callback[0] + ) + + def _set_color(self, key, pressed): + if pressed: + self.macropad.pixels[key] = int(0x010101) + else: + color = self.colors[key] + self.macropad.pixels[key] = int( self.colors[key] or 0 ) + for option in self.options: + self.macropad.pixels[option.selected] = int(option.color) + + def tick(self): + "Execute asyncronous events" + + Action().collect_tasks() + if self.next_tick is None or ticks.less(self.next_tick): + return + + self.periodic_callback[1](self.macropad) + self._update_tick() + + def start(self): + self.position = self.macropad.encoder + + for key_index in range(12): + x = key_index % 3 + y = key_index // 3 + self.group.append(label.Label(terminalio.FONT, text='', color=0xFFFFFF, + anchored_position=((self.macropad.display.width - 1) * x / 2, + self.macropad.display.height - 1 - + (3 - y) * 12), + anchor_point=(x / 2, 1.0))) + self.group.append(Rect(0, 0, self.macropad.display.width, 12, fill=0xFFFFFF)) + self.group.append(label.Label(terminalio.FONT, text='', color=0x000000, + anchored_position=(self.macropad.display.width//2, -2), + anchor_point=(0.5, 0.0))) + self.macropad.display.show(self.group) + + self.group[13].text = self.name # Application name + for i in range(12): + if self.keys[i] is not None: + self.group[i].text = self.keys[i][0] + self.macropad.keyboard.release_all() + self.macropad.consumer_control.release() + self.macropad.mouse.release_all() + self.macropad.stop_tone() + for key in range(12): + self._set_color(key, False) + self.macropad.pixels.show() + self.macropad.display.refresh() + + def execute_action(self, key, pressed): + + for option in self.options: + option.selected = key + + action = self.keys[key] + + # Do nothing if there is no action for this key + if action is None: + return + + # Update the leds + if key < 12: + if pressed: Action().flash(key, 0x010101) + else: self._set_color(key, pressed) + else: + for key_idx in range(12): + self._set_color(key_idx, pressed) + self.macropad.pixels.show() + + result = action[1](self.macropad, key, pressed) + + # If we use the rotary encoder, mark all the actions as resolved. + if key >= 12: + action[1](self.macropad, key, False) + for key_idx in range(12): + self._set_color(key_idx, False) + self.macropad.pixels.show() + + return result diff --git a/src/ticks.py b/src/ticks.py new file mode 100644 index 0000000..ba6831a --- /dev/null +++ b/src/ticks.py @@ -0,0 +1,21 @@ +from supervisor import ticks_ms + +# See the example at +# https://docs.circuitpython.org/en/latest/shared-bindings/supervisor/index.html?#supervisor.ticks_ms +_TICKS_PERIOD = const(1<<29) +_TICKS_MAX = const(_TICKS_PERIOD-1) +_TICKS_HALFPERIOD = const(_TICKS_PERIOD//2) + +def add(ticks, delta): + "Add a delta to a base number of ticks, performing wraparound at 2**29ms." + return (ticks + delta) % _TICKS_PERIOD + +def ticks_diff(ticks1, ticks2): + "Compute the signed difference between two ticks values, assuming that they are within 2**28 ticks" + diff = (ticks1 - ticks2) & _TICKS_MAX + diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD + return diff + +def less(ticks): + "Return true iff ticks1 is less than ticks2, assuming that they are within 2**28 ticks" + return ticks_diff(ticks_ms(), ticks) < 0 -- cgit v1.2.3