Initial public release
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
"""Shared constants and key mappings for the ATOMS3 MacroPad app."""
|
||||
|
||||
import os
|
||||
|
||||
APP_NAME = "ATOMS3 MacroPad"
|
||||
APP_VERSION = "1.0.0"
|
||||
DEVICE_ID = "ATOMS3-MACROPAD"
|
||||
ESPRESSIF_VID = 0x303A
|
||||
|
||||
SCREEN_W = 128
|
||||
SCREEN_H = 128
|
||||
|
||||
# Project-relative storage. utils/constants.py → repo root → config/
|
||||
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
APPDATA_DIR = os.path.join(_PROJECT_ROOT, "config")
|
||||
PROJECT_FILE = os.path.join(APPDATA_DIR, "project.json")
|
||||
IMAGES_DIR = os.path.join(APPDATA_DIR, "images")
|
||||
PROFILES_DIR = os.path.join(APPDATA_DIR, "profiles")
|
||||
PROFILES_META_FILE = os.path.join(APPDATA_DIR, "profiles_meta.json")
|
||||
BACKUPS_DIR = os.path.join(APPDATA_DIR, "backups")
|
||||
DEFAULT_PROFILE_NAME = "Default"
|
||||
|
||||
DEFAULT_HOLD_MS = 500
|
||||
DEFAULT_TYPE_DELAY = 15
|
||||
DEFAULT_ORIENTATION = 0
|
||||
DEFAULT_RESUME_DELAY = 0 # seconds, 0 = disabled
|
||||
DEFAULT_COMBO_PRE_MS = 500 # ms delay before key combo is sent
|
||||
DEFAULT_COMBO_POST_MS = 500 # ms delay after key combo is sent
|
||||
DEFAULT_PROBE_TIMEOUT_MS = 300 # ms to wait for host LED response in PC Alive Check
|
||||
DEFAULT_MEDIA_HOLD_MS = 100 # ms to hold media key before release
|
||||
DEFAULT_TYPE_SHIFT_EXTRA_MS = 25 # extra ms per shifted char (uppercase, !@#$ etc.)
|
||||
DEFAULT_TYPE_SETTLE_MS = 150 # ms after last char to let HID reports drain
|
||||
|
||||
# Pause-screen text-box margins (pixels at the device's 128x128 LCD).
|
||||
# These control where the pause-text wrapping/truncation box sits inside
|
||||
# the screen and are mirrored 1:1 into the firmware's drawWrapped() call.
|
||||
DEFAULT_PAUSE_MARGIN_LEFT = 4
|
||||
DEFAULT_PAUSE_MARGIN_RIGHT = 4
|
||||
DEFAULT_PAUSE_MARGIN_TOP = 16
|
||||
DEFAULT_PAUSE_MARGIN_BOTTOM = 12
|
||||
|
||||
# Device LCD dimensions — used by the pause preview to render at the
|
||||
# same proportions as the device.
|
||||
DEVICE_SCREEN_W = 128
|
||||
DEVICE_SCREEN_H = 128
|
||||
|
||||
NODE_TYPES = {
|
||||
"start": {"label": "Start", "color": "#2ECC71", "desc": "Routine starts here"},
|
||||
"text": {"label": "Type Text", "color": "#4A90D9", "desc": "Type a string of text"},
|
||||
"combo": {"label": "Key Combo", "color": "#D94A4A", "desc": "Press a key combination"},
|
||||
"pause": {"label": "Pause", "color": "#D9A84A", "desc": "Wait for click or timer"},
|
||||
"branch": {"label": "Branch", "color": "#9B59B6", "desc": "Choose between paths"},
|
||||
"delay": {"label": "Delay", "color": "#7F8C8D", "desc": "Wait milliseconds"},
|
||||
"repeat": {"label": "Loop", "color": "#27AE60", "desc": "Repeat nodes N times"},
|
||||
"loop_selector": {"label": "Loop Selector", "color": "#F1C40F", "desc": "Prompt user for loop count"},
|
||||
"iteration_branch": {"label": "Iteration Branch", "color": "#8E44AD", "desc": "Branch based on loop iteration number"},
|
||||
"note": {"label": "Note", "color": "#555555", "desc": "On-canvas annotation (not sent to device)"},
|
||||
"aggregator": {"label": "Aggregator", "color": "#5D6D7E", "desc": "Merge multiple paths into one output"},
|
||||
"mouse": {"label": "Mouse Click", "color": "#E67E22", "desc": "Send mouse button"},
|
||||
"media": {"label": "Media Key", "color": "#1ABC9C", "desc": "Media control key"},
|
||||
"bluetooth": {"label": "Variables", "color": "#0077CC", "desc": "Read/write variables (BLE or local)"},
|
||||
"rs232": {"label": "RS232 Send", "color": "#8B5CF6", "desc": "Send data via RS232 serial"},
|
||||
"subroutine": {"label": "Sub-Routine", "color": "#FF6B9D", "desc": "Call a reusable sub-routine"},
|
||||
"pc_alive_check": {"label": "PC Alive Check", "color": "#F59E0B", "desc": "Check if host PC is responding"},
|
||||
"macro": {"label": "Macro", "color": "#14B8A6", "desc": "Replay an exact recorded key-press sequence"},
|
||||
}
|
||||
|
||||
SUBROUTINES_PROFILE_NAME = "Sub-Routines"
|
||||
|
||||
RS232_BAUD_RATES = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]
|
||||
RS232_DATA_BITS = [5, 6, 7, 8]
|
||||
RS232_STOP_BITS = ["1", "1.5", "2"]
|
||||
RS232_PARITY = ["none", "even", "odd"]
|
||||
RS232_LINE_ENDINGS = [("None", "none"), ("CR (\\r)", "cr"), ("LF (\\n)", "lf"), ("CRLF (\\r\\n)", "crlf")]
|
||||
|
||||
MODIFIER_KEYS = ["ctrl", "shift", "alt", "gui", "rctrl", "rshift", "ralt", "rgui"]
|
||||
|
||||
MODIFIER_LABELS = {
|
||||
"ctrl": "Ctrl", "shift": "Shift", "alt": "Alt", "gui": "Win",
|
||||
"rctrl": "Right Ctrl", "rshift": "Right Shift", "ralt": "AltGr", "rgui": "Right Win",
|
||||
}
|
||||
|
||||
SPECIAL_KEYS = [
|
||||
"enter", "esc", "backspace", "tab", "space", "delete", "insert",
|
||||
"home", "end", "pageup", "pagedown",
|
||||
"up", "down", "left", "right",
|
||||
"capslock", "numlock", "scrolllock", "printscreen", "pause", "menu",
|
||||
"f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
|
||||
"f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24",
|
||||
]
|
||||
|
||||
MOUSE_BUTTONS = ["left", "right", "middle"]
|
||||
MOUSE_ACTIONS = ["click", "double", "press", "release"]
|
||||
|
||||
MEDIA_ACTIONS = [
|
||||
("vol_up", "Volume Up"), ("vol_down", "Volume Down"), ("mute", "Mute"),
|
||||
("play_pause", "Play/Pause"), ("next", "Next Track"), ("prev", "Previous Track"),
|
||||
("stop", "Stop"), ("brightness_up", "Brightness Up"), ("brightness_down", "Brightness Down"),
|
||||
]
|
||||
|
||||
ORIENTATION_OPTIONS = [
|
||||
(0, "Normal (0\u00b0)"),
|
||||
(1, "90\u00b0"),
|
||||
(2, "180\u00b0"),
|
||||
(3, "270\u00b0"),
|
||||
]
|
||||
|
||||
# (name, GUI hex preview)
|
||||
DISPLAY_COLORS = [
|
||||
("white", "#FFFFFF"),
|
||||
("red", "#FF4444"),
|
||||
("green", "#44FF88"),
|
||||
("blue", "#4488FF"),
|
||||
("yellow", "#FFFF44"),
|
||||
("cyan", "#44FFFF"),
|
||||
("magenta", "#FF44FF"),
|
||||
("orange", "#FFA040"),
|
||||
]
|
||||
|
||||
# HID USB usage codes for the "macro" recording node.
|
||||
# Macro events are stored as [t_ms, action, hid_code] tuples. Using raw HID
|
||||
# usage codes (not ASCII) lets us press/release each physical key independently
|
||||
# — essential when modifiers and letters overlap or when multiple keys are
|
||||
# held at once. The firmware calls keyboard.pressRaw() / keyboard.releaseRaw()
|
||||
# with these codes directly, so modifiers never get retyped out from under a
|
||||
# chord.
|
||||
#
|
||||
# Map from Tkinter keysym → HID USB usage code (standard 104-key layout).
|
||||
# Keysym ordering reference: https://www.tcl.tk/man/tcl/TkCmd/keysyms.html
|
||||
TKKEYSYM_TO_HID = {
|
||||
# --- Letters (both cases map to the same physical key) ---
|
||||
**{chr(c): 0x04 + (c - ord('a')) for c in range(ord('a'), ord('z') + 1)},
|
||||
**{chr(c): 0x04 + (c - ord('A')) for c in range(ord('A'), ord('Z') + 1)},
|
||||
# --- Top-row digits (1..0) ---
|
||||
"1": 0x1E, "2": 0x1F, "3": 0x20, "4": 0x21, "5": 0x22,
|
||||
"6": 0x23, "7": 0x24, "8": 0x25, "9": 0x26, "0": 0x27,
|
||||
# --- Whitespace / edit ---
|
||||
"Return": 0x28, "KP_Enter": 0x58,
|
||||
"Escape": 0x29, "BackSpace": 0x2A, "Tab": 0x2B, "space": 0x2C,
|
||||
# --- Punctuation (US layout shift-pair maps to same key) ---
|
||||
"minus": 0x2D, "underscore": 0x2D,
|
||||
"equal": 0x2E, "plus": 0x2E,
|
||||
"bracketleft": 0x2F, "braceleft": 0x2F,
|
||||
"bracketright": 0x30, "braceright": 0x30,
|
||||
"backslash": 0x31, "bar": 0x31,
|
||||
"semicolon": 0x33, "colon": 0x33,
|
||||
"apostrophe": 0x34, "quotedbl": 0x34,
|
||||
"grave": 0x35, "asciitilde": 0x35,
|
||||
"comma": 0x36, "less": 0x36,
|
||||
"period": 0x37, "greater": 0x37,
|
||||
"slash": 0x38, "question": 0x38,
|
||||
# Shifted digits — map to the underlying digit key
|
||||
"exclam": 0x1E, "at": 0x1F, "numbersign": 0x20, "dollar": 0x21,
|
||||
"percent": 0x22, "asciicircum": 0x23, "ampersand": 0x24,
|
||||
"asterisk": 0x25, "parenleft": 0x26, "parenright": 0x27,
|
||||
# --- Locks / system ---
|
||||
"Caps_Lock": 0x39,
|
||||
"Num_Lock": 0x53,
|
||||
"Scroll_Lock": 0x47,
|
||||
"Print": 0x46,
|
||||
"Pause": 0x48,
|
||||
"Menu": 0x65,
|
||||
# --- Navigation ---
|
||||
"Insert": 0x49, "Home": 0x4A, "Prior": 0x4B,
|
||||
"Delete": 0x4C, "End": 0x4D, "Next": 0x4E,
|
||||
"Right": 0x4F, "Left": 0x50, "Down": 0x51, "Up": 0x52,
|
||||
# --- Function keys ---
|
||||
**{f"F{i}": 0x3A + (i - 1) for i in range(1, 13)}, # F1..F12 → 0x3A..0x45
|
||||
**{f"F{i}": 0x68 + (i - 13) for i in range(13, 25)}, # F13..F24 → 0x68..0x73
|
||||
# --- Modifiers (0xE0..0xE7) ---
|
||||
"Control_L": 0xE0, "Shift_L": 0xE1, "Alt_L": 0xE2, "Super_L": 0xE3,
|
||||
"Control_R": 0xE4, "Shift_R": 0xE5, "Alt_R": 0xE6, "Super_R": 0xE7,
|
||||
# Numpad
|
||||
"KP_Divide": 0x54, "KP_Multiply": 0x55, "KP_Subtract": 0x56,
|
||||
"KP_Add": 0x57, "KP_Decimal": 0x63,
|
||||
**{f"KP_{i}": 0x59 + (i - 1) for i in range(1, 10)}, # KP_1..KP_9 → 0x59..0x61
|
||||
"KP_0": 0x62,
|
||||
}
|
||||
|
||||
def hid_code_label(code: int) -> str:
|
||||
"""Friendly short label for a HID usage code (for the macro event list)."""
|
||||
# Prefer a pretty name over the raw keysym
|
||||
PRETTY = {
|
||||
0x28: "Enter", 0x29: "Esc", 0x2A: "Backspace", 0x2B: "Tab", 0x2C: "Space",
|
||||
0x4C: "Delete", 0x49: "Insert", 0x4A: "Home", 0x4D: "End",
|
||||
0x4B: "PageUp", 0x4E: "PageDown",
|
||||
0x4F: "→", 0x50: "←", 0x51: "↓", 0x52: "↑",
|
||||
0x39: "CapsLock", 0x53: "NumLock", 0x47: "ScrollLock",
|
||||
0x46: "PrtSc", 0x48: "Pause", 0x65: "Menu",
|
||||
0x58: "KP Enter", 0x54: "KP /", 0x55: "KP *", 0x56: "KP -",
|
||||
0x57: "KP +", 0x63: "KP .",
|
||||
0xE0: "LCtrl", 0xE1: "LShift", 0xE2: "LAlt", 0xE3: "LWin",
|
||||
0xE4: "RCtrl", 0xE5: "RShift", 0xE6: "RAlt", 0xE7: "RWin",
|
||||
}
|
||||
if code in PRETTY:
|
||||
return PRETTY[code]
|
||||
if 0x04 <= code <= 0x1D:
|
||||
return chr(ord('A') + (code - 0x04))
|
||||
if 0x1E <= code <= 0x26:
|
||||
return str(1 + (code - 0x1E))
|
||||
if code == 0x27:
|
||||
return "0"
|
||||
if 0x3A <= code <= 0x45:
|
||||
return f"F{1 + (code - 0x3A)}"
|
||||
if 0x68 <= code <= 0x73:
|
||||
return f"F{13 + (code - 0x68)}"
|
||||
if 0x59 <= code <= 0x61:
|
||||
return f"KP{1 + (code - 0x59)}"
|
||||
if code == 0x62:
|
||||
return "KP0"
|
||||
PUNCT = {
|
||||
0x2D: "-", 0x2E: "=", 0x2F: "[", 0x30: "]", 0x31: "\\",
|
||||
0x33: ";", 0x34: "'", 0x35: "`", 0x36: ",", 0x37: ".", 0x38: "/",
|
||||
}
|
||||
return PUNCT.get(code, f"0x{code:02X}")
|
||||
|
||||
|
||||
CANVAS_BG = "#1E1E2E"
|
||||
NODE_HEADER_HEIGHT = 24
|
||||
NODE_MIN_WIDTH = 160
|
||||
NODE_PORT_RADIUS = 6
|
||||
NODE_BODY_COLOR = "#2D2D3D"
|
||||
NODE_SELECTED_BORDER = "#FFFFFF"
|
||||
NODE_UPSTREAM_BORDER = "#22C55E" # green — matches input-port color (neighbor wires INTO our input)
|
||||
NODE_DOWNSTREAM_BORDER = "#EF4444" # red — matches output-port color (neighbor is wired FROM our output)
|
||||
# Backwards-compatible alias; older code may still import this name.
|
||||
NODE_CONNECTED_BORDER = NODE_UPSTREAM_BORDER
|
||||
NODE_TEXT_COLOR = "#FFFFFF"
|
||||
NODE_PORT_IN_COLOR = "#4ADE80"
|
||||
NODE_PORT_OUT_COLOR = "#F87171"
|
||||
WIRE_COLOR = "#6B7280"
|
||||
WIRE_SELECTED_COLOR = "#FBBF24"
|
||||
GRID_SIZE = 20
|
||||
GRID_COLOR = "#2A2A3A"
|
||||
Reference in New Issue
Block a user