From 3bd77c9e826316fb51fe7d56e75d8d6ac72e66c9 Mon Sep 17 00:00:00 2001
From: Serge Bazanski <q3k@q3k.org>
Date: Thu, 3 Aug 2023 19:02:08 +0200
Subject: [PATCH] py: remove st3m

---
 python_payload/st3m/__init__.py    |   0
 python_payload/st3m/application.py | 169 ------------
 python_payload/st3m/control.py     | 146 -----------
 python_payload/st3m/event.py       | 369 --------------------------
 python_payload/st3m/menu.py        | 400 -----------------------------
 python_payload/st3m/ui.py          | 296 ---------------------
 python_payload/st3m/utils.py       |  47 ----
 7 files changed, 1427 deletions(-)
 delete mode 100644 python_payload/st3m/__init__.py
 delete mode 100644 python_payload/st3m/application.py
 delete mode 100644 python_payload/st3m/control.py
 delete mode 100644 python_payload/st3m/event.py
 delete mode 100644 python_payload/st3m/menu.py
 delete mode 100644 python_payload/st3m/ui.py
 delete mode 100644 python_payload/st3m/utils.py

diff --git a/python_payload/st3m/__init__.py b/python_payload/st3m/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/python_payload/st3m/application.py b/python_payload/st3m/application.py
deleted file mode 100644
index 1cf755c804..0000000000
--- a/python_payload/st3m/application.py
+++ /dev/null
@@ -1,169 +0,0 @@
-from st3m import logging
-
-log = logging.Log(__name__, level=logging.INFO)
-log.info("import")
-from . import ui, event, menu
-
-STATE_OFF = 0
-STATE_INIT = 10
-STATE_BACKGROUND = 200
-STATE_FOREGROUND = 300
-STATE_ERROR = 500
-
-log.info("setting up application")
-
-
-class Application:
-    def __init__(
-        self, title="badge23 app", author="someone@earth", exit_on_menu_enter=True
-    ):
-        log.info(f"__init__ app '{title}'")
-        self.title = title
-        self.author = author
-        self.state = STATE_OFF
-        self.has_background = False
-        self.has_foreground = True
-        self._events_background = []
-        self._events_foreground = []
-        self.ui = ui.Viewport()
-        self.icon = ui.Icon(label=self.title, size=100)
-        self.engine = event.the_engine
-        self.menu = None
-        if exit_on_menu_enter:
-            self.add_event(
-                event.Event(
-                    name="exit",
-                    action=self.exit,
-                    condition=lambda e: e["type"] == "button"
-                    and e["index"] == 1
-                    and e.get("from") == 2
-                    and e["change"],
-                )
-            )
-
-    def __repr__(self):
-        return "App " + self.title
-
-    def init(self):
-        log.info(f"init app '{self.title}'")
-        self.state = STATE_INIT
-        self.on_init()
-        if self.has_background:
-            if self._events_background:
-                self._set_events(self._events_background, True)
-            engine.register_service_loop(self.main_always, True)
-
-    def run(self):
-        log.info(f"run app '{self.title}' from state {self.state}")
-        if self.state == STATE_OFF:
-            log.info("from STATE_OFF, doing init()")
-            self.init()
-        if self.has_foreground:
-            self._to_foreground()
-        elif self.has_background:
-            self._to_background()
-        else:
-            log.warning(
-                f"App {self.title} has neither foreground nor background, not doing anything"
-            )
-
-        # start the eventloop if it is not already running
-        if not event.the_engine.is_running:
-            log.info("eventloop not yet running, starting")
-            event.the_engine.eventloop()
-
-    def exit(self, data={}):
-        log.info(f"exit app '{self.title}' from state {self.state}")
-        self.on_exit()
-        if self.state == STATE_FOREGROUND:
-            self._to_background()
-
-    def kill(self):
-        # disable all events
-        log.info(f"kill app '{self.title}' from state {self.state}")
-        engine.register_service_loop(self.main_always, False)
-
-        engine.foreground_app = None
-        self._set_events(self._events_background, False)
-        self._set_events(self._events_forground, False)
-        self.state = STATE_OFF
-
-    def draw(self, ctx):
-        self.ui.draw(ctx)
-        self.on_draw(ctx)
-
-    def tick(self):
-        self.main_foreground()
-
-    def add_event(self, event, is_background=False):
-        if not is_background:
-            self._events_foreground.append(event)
-        else:
-            self._events_background.append(event)
-
-    def is_foreground(self):
-        return self.state == STATE_FOREGROUND
-
-    def _to_foreground(self):
-        log.info(f"to_foreground app '{self.title}' from state {self.state}")
-        if not self.has_foreground:
-            log.error(f"app has no foreground!")
-            return
-
-        if self._events_background:
-            self._set_events(self_events_background, False)
-        self.state = STATE_FOREGROUND
-
-        if self._events_foreground:
-            self._set_events(self._events_foreground, True)
-
-        # TODO(q3k): make this pending
-        # self.ui.ctx.rgb(*ui.BLACK).rectangle(-120,-120,240,240).fill()
-        # self.icon.draw()
-
-        self.on_foreground()
-
-        self.engine.foreground_app = self
-
-    def _to_background(self):
-        log.info(f"to_background app '{self.title}' from state {self.state}")
-        self.state = STATE_BACKGROUND
-        if self._events_foreground:
-            self._set_events(self._events_foreground, False)
-
-        self.engine.foreground_app = None
-        menu.menu_back()
-        if self.has_background:
-            self.state = STATE_BACKGROUND
-        self.on_background()
-
-    def _set_events(self, events, enabled=True):
-        for e in events:
-            e.set_enabled(enabled)
-
-    def on_init(self):
-        log.info(f"app {self.title}: on_init()")
-
-    def on_foreground(self):
-        log.info(f"app {self.title}: on_foreground()")
-
-    def on_background(self):
-        log.info(f"app {self.title}: on_background()")
-
-    def on_exit(self):
-        log.info(f"app {self.title}: on_exit()")
-
-    def on_kill(self):
-        log.info(f"app {self.title}: on_kill()")
-
-    def on_draw(self, ctx):
-        log.debug(f"app {self.title}: on_draw()")
-
-    def main_foreground(self):
-        pass
-
-    def main_always(self):
-        pass
-
-    def main(self):
-        self.main_foreground()
diff --git a/python_payload/st3m/control.py b/python_payload/st3m/control.py
deleted file mode 100644
index 1fcc2b1273..0000000000
--- a/python_payload/st3m/control.py
+++ /dev/null
@@ -1,146 +0,0 @@
-from st3m import logging, menu
-
-log = logging.Log(__name__, level=logging.INFO)
-log.info("import")
-
-from . import ui
-
-
-class Control:
-    def __init__(self, name, default=0, on_set=None, on_get=None, on_mod=None):
-        # TODO inheritance from Control()
-        self.name = name
-        self.on_set = on_set
-        self.on_get = on_get
-        self.on_mod = on_mod
-
-        if not self.on_get:
-            self._value = default
-        self.ui = ui.IconValue(label=self.name, size=60, value=self.get_value())
-        self.ui.get_value = self.get_normal_value
-
-        self.menu = menu.MenuControl(self)
-
-    def draw(self, ctx):
-        self.ui.value = self.get_value()
-        self.ui.draw(ctx)
-
-    def get_normal_value(self):
-        v = self.get_value()
-        n = min(1, max(0, v))
-        return n
-
-    def get_value(self, update=True):
-        if update and self.on_get:
-            self._value = self.on_get()
-        return self._value
-
-    def set_value(self, value, do_trigger=True):
-        self._value = value
-        if do_trigger:
-            if self.on_set:
-                self.on_set(value)
-
-    def mod_value(self, delta):
-        self._value = self.on_mod(delta)
-
-    def enter_menu(self):
-        menu.submenu_push(self.menu)
-
-    def scroll(self, delta):
-        pass
-
-    def touch_1d(self, x, z):
-        pass
-
-
-class ControlSwitch(Control):
-    def enter(self):
-        self.set_value(not self.get_value())
-
-    def scroll(self, delta):
-        self.enter()
-
-    def touch_1d(self, x, z):
-        if z < 0:  # Release
-            self.enter()
-
-
-class ControlFloat(Control):
-    def __init__(self, min=0.0, max=1.0, step=0.1, *args, **kwargs):
-        self.min = min
-        self.max = max
-        self.step = step
-        super().__init__(*args, **kwargs)
-
-    def get_normal_value(self):
-        v = self.get_value()
-        n = (v - self.min) / (self.max - self.min)
-        return n
-
-
-class ControlKnob(ControlFloat):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self._x_init = 0
-        self._touch_started_here = False
-
-    def enter(self):
-        # repeat action with current value
-        self.set_value(self.get_value())
-
-    def scroll(self, delta):
-        if self.on_mod:
-            # print("control: on mod")
-            self.on_mod(delta)
-
-        elif self.on_set:
-            v = self.get_value()
-            v_new = max(self.min, min(self.max, v + delta * self.step))
-            self.set_value(v_new)
-
-    def touch_1d(self, x, z):
-        if z > 0:  # Inital Contact
-            self._x_init = x
-            self._touch_started_here = True
-
-        if z == 0:  # Continous contact
-            if not self._touch_started_here:
-                return
-            diff = self._x_init - x
-            self.scroll(5 * diff)
-
-        if z < 0:  # Release
-            self._touch_started_here = False
-
-
-class ControlSlide(ControlFloat):
-    def __init__(self, do_reset=True, *args, **kwargs):
-        self.do_reset = do_reset
-        self._saved_value = None
-        super().__init__(*args, **kwargs)
-
-    def touch_1d(self, x, z):
-        if z > 0:  # Inital Contact
-            self._saved_value = self.get_value()
-
-        if self._saved_value is None:
-            return
-
-        if z == 0:  # Continous contact
-            v = (self.max - self.min) * x + self.min
-            self.set_value(v)
-            # print("c",x,v,self.max,self.min)
-
-        if z < 0:  # Release
-            if self.do_reset:
-                self.set_value(self._saved_value)
-            self._saved_value = None
-
-
-class ControlString(Control):
-    pass
-
-
-class ControlTextField:
-    pass
diff --git a/python_payload/st3m/event.py b/python_payload/st3m/event.py
deleted file mode 100644
index e48505e3a6..0000000000
--- a/python_payload/st3m/event.py
+++ /dev/null
@@ -1,369 +0,0 @@
-from st3m import logging
-
-log = logging.Log(__name__, level=logging.INFO)
-log.info("import")
-
-from st3m.system import hardware, captouch
-
-import kernel
-import time
-import math
-import random
-
-
-EVENTTYPE_TIMED = 1
-EVENTTYPE_INPUT = 2
-# EVENTTYPE_BUTTON = 2
-# EVENTTYPE_CAPTOUCH = 3
-# EVENTTYPE_CAPCROSS = 4
-
-
-class Engine:
-    def __init__(self):
-        self.events_timed = []
-        self.events_input = []
-        self.next_timed = None
-        self.last_input_state = None
-        self.userloop = None
-        self.is_running = False
-        self.foreground_app = None
-        self.active_menu = None
-        self.overlay = None
-
-        self._draw_started = None
-        self._draw_ended = None
-
-        self._think_started = None
-        self._think_ended = None
-
-        self._last_report = None
-
-    def _report_heap(self):
-        st = kernel.heap_stats()
-        g = st.general
-        d = st.dma
-        log.info(
-            f"Heap: General: {g.total_free_bytes:d}B free, {g.total_allocated_bytes:d}B allocated, {g.largest_free_block:d}B largest free block"
-        )
-        log.info(
-            f"Heap: DMA: {d.total_free_bytes:d}B free, {d.total_allocated_bytes:d}B allocated, {d.largest_free_block:d}B largest free block"
-        )
-
-    def _report(self):
-        now = time.ticks_ms()
-        if self._last_report is not None and (now - self._last_report) < 1000:
-            return
-
-        if self._draw_started is not None and self._draw_ended is not None:
-            draw_time = self._draw_ended - self._draw_started
-            log.info(f"EventLoop Draw time: {draw_time:.3f}ms")
-
-        if self._think_started is not None and self._think_ended is not None:
-            think_time = self._think_ended - self._think_started
-            log.info(f"EventLoop Think time: {think_time:.3f}ms")
-
-        self._report_heap()
-        self._last_report = now
-
-    def add(self, event):
-        if isinstance(event, EventTimed):
-            self.add_timed(event)
-        elif isinstance(event, Event):
-            self.add_input(event)
-
-    def add_timed(self, event):
-        self.events_timed.append(event)
-        self._sort_timed()
-
-    def add_input(self, event):
-        self.events_input.append(event)
-
-    def remove(self, group_id):
-        self.remove_input(group_id)
-        self.remove_timed(group_id)
-
-    def remove_timed(self, group_id):
-        # print("before:",len(self.events_timed))
-        self.events_timed = [
-            event for event in self.events_timed if event.group_id != group_id
-        ]
-        self._sort_timed()
-        # print("after",len(self.events_timed))
-
-    def remove_input(self, group_id):
-        self.events_input = [
-            event for event in self.events_input if event.group_id != group_id
-        ]
-
-    def _sort_timed(self):
-        self.events_timed = sorted(self.events_timed, key=lambda event: event.deadline)
-
-    def _handle_timed(self):
-        if not self.next_timed and self.events_timed:
-            self.next_timed = self.events_timed.pop(0)
-
-        now = time.ticks_ms()
-
-        if self.next_timed:
-            diff = time.ticks_diff(self.next_timed.deadline, now)
-            if diff <= 0:
-                self.next_timed.trigger({"ticks_ms": now, "ticks_delay": -diff})
-                self.next_timed = None
-
-    def _handle_input(self, delta):
-        # log.info("input {delta}")
-        input_state = []
-
-        # buttons
-        input_state.append(
-            {"type": "button", "index": 0, "value": hardware.menu_button_get()}
-        )
-
-        input_state.append(
-            {"type": "button", "index": 1, "value": hardware.application_button_get()}
-        )
-
-        # captouch
-        cps = captouch.read()
-        for i in range(0, 10):
-            petal = cps.petals[i]
-            (radius, angle) = petal.position
-            input_state.append(
-                {
-                    "type": "captouch",
-                    "index": i,
-                    "value": petal.pressed,
-                    "radius": radius,
-                    "angle": angle,
-                }
-            )
-
-        if not self.last_input_state:
-            self.last_input_state = input_state
-            return
-
-        for i in range(len(input_state)):
-            entry = input_state[i]
-            last_entry = self.last_input_state[i]
-
-            # update for all
-            entry["ticks_ms"] = time.ticks_ms()
-            entry["delta"] = delta
-
-            if entry["value"] != last_entry["value"]:
-                # update only when value changed
-                entry["change"] = True
-                entry["from"] = last_entry["value"]
-            else:
-                # update only when value did not change
-                entry["change"] = False
-
-            # find and trigger the events q
-            triggered_events = list(
-                filter(lambda e: e.enabled and e.condition(entry), self.events_input)
-            )
-            # log.info(triggered_events)
-            # map(lambda e: e.trigger(d), triggered_events)
-            for e in triggered_events:
-                e.trigger(entry)
-
-        self.last_input_state = input_state
-
-    def _handle_userloop(self):
-        if self.foreground_app:
-            self.foreground_app.tick()
-
-    def _handle_draw(self, ctx):
-        self._draw_started = time.ticks_ms()
-
-        if self.foreground_app:
-            self.foreground_app.draw(ctx)
-        if self.active_menu:
-            self.active_menu.draw(ctx)
-        if self.overlay:
-            self.overlay.draw(ctx)
-
-        self._draw_ended = time.ticks_ms()
-
-    def _eventloop_single(self, delta):
-        self._think_started = time.ticks_ms()
-
-        self._handle_timed()
-        self._handle_input(delta)
-        self._handle_userloop()
-
-        self._think_ended = time.ticks_ms()
-
-    def eventloop(self):
-        log.info("starting eventloop")
-        if self.is_running:
-            log.warning("eventloop already running, doing nothing")
-            return
-        self.is_running = True
-
-        last_eventloop = None
-
-        ctx = None
-        while self.is_running:
-            start = time.ticks_ms()
-            deadline = start + 20
-            self._report()
-
-            if last_eventloop is not None:
-                delta = (start - last_eventloop) / 1000.0
-                if delta >= 0.01:
-                    last_eventloop = start
-                    self._eventloop_single(delta)
-            else:
-                last_eventloop = start
-
-            post_think = time.ticks_ms()
-
-            if ctx is None:
-                ctx = hardware.get_ctx()
-                if ctx is not None:
-                    self._handle_draw(ctx)
-
-            post_draw = time.ticks_ms()
-
-            if ctx is not None and not hardware.display_pipe_full():
-                hardware.display_update(ctx)
-                # log.info("update")
-                ctx = None
-
-            post_submit = time.ticks_ms()
-
-            wait = deadline - time.ticks_ms()
-            if wait > 0:
-                hardware.freertos_sleep(wait)
-            else:
-                log.warning(f"Application took too long too process! Slack {wait}ms.")
-                log.warning(
-                    f"Think: {post_think-start}ms, Draw: {post_draw-post_think}ms, Submit: {post_submit-post_draw}ms"
-                )
-                hardware.freertos_sleep(1)
-
-
-class Event:
-    def __init__(
-        self,
-        name="unknown",
-        data={},
-        action=None,
-        condition=None,
-        group_id=None,
-        enabled=False,
-    ):
-        # print (action)
-        self.name = name
-        self.eventtype = None
-        self.data = data
-        self.action = action
-        self.condition = condition
-        self.enabled = enabled
-        if not condition:
-            self.condition = lambda x: True
-        self.group_id = group_id
-
-        if enabled:
-            self.set_enabled()
-
-    def trigger(self, triggerdata={}):
-        log.debug("triggered {} (with {})".format(self.name, triggerdata))
-        if not self.action is None:
-            triggerdata.update(self.data)
-            self.action(triggerdata)
-
-    def set_enabled(self, enabled=True):
-        self.enabled = enabled
-        if enabled:
-            the_engine.add(self)
-        else:
-            self.remove()
-
-    def remove(self):
-        log.info(f"remove {self}")
-        while self in the_engine.events_input:
-            # print ("from input")
-            the_engine.events_input.remove(self)
-        while self in the_engine.events_timed:
-            # print("from timed")
-            the_engine.events_timed.remove(self)
-            the_engine._sort_timed()
-
-
-class EventTimed(Event):
-    def __init__(self, ms, name="timer", *args, **kwargs):
-        # super().__init__(name,data,action)
-        self.deadline = time.ticks_add(time.ticks_ms(), ms)
-
-        super().__init__(*args, **kwargs)
-        self.name = name
-        self.type = EVENTTYPE_TIMED
-
-    def __repr__(self):
-        return "event on tick {} ({})".format(self.deadline, self.name)
-
-
-# hack, make this oo
-def on_restart(data):
-    print("loop sequence")
-    obj = data["object"]
-    if obj.is_running:
-        obj.start()
-
-
-class Sequence:
-    def __init__(self, bpm=60, loop=True, steps=16, action=None):
-        self.group_id = random.randint(0, 100000000)
-        self.bpm = bpm
-        self.steps = steps
-        self.repeat_event = None
-        self.loop = loop
-        self.events = []
-        self.is_running = False
-
-        if not action:
-            self.action = lambda data: log.info("step {}".format(data.get("step")))
-        else:
-            self.action = action
-
-    def start(self):
-        if self.is_running:
-            self.stop()
-        stepsize_ms = int(60 * 1000 / self.bpm)
-        for i in range(self.steps):
-            log.debug(f"adding sequence event {i}")
-            self.events.append(
-                EventTimed(
-                    stepsize_ms * i,
-                    name="seq{}".format(i),
-                    action=self.action,
-                    data={"step": i},
-                    group_id=self.group_id,
-                    enabled=True,
-                )
-            )
-        if self.loop:
-            self.repeat_event = EventTimed(
-                stepsize_ms * self.steps,
-                name="loop",
-                group_id=self.group_id,
-                enabled=True,
-                action=on_restart,
-                data={"object": self},
-            )
-        self.is_running = True
-
-    def stop(self):
-        # for e in self.events: e.remove()
-        log.info("sequence stop")
-        the_engine.remove_timed(group_id=self.group_id)
-        self.events = []
-        if self.repeat_event:
-            self.repeat_event.remove()
-        self.is_running = False
-
-
-global the_engine
-the_engine = Engine()
diff --git a/python_payload/st3m/menu.py b/python_payload/st3m/menu.py
deleted file mode 100644
index d9f3db7209..0000000000
--- a/python_payload/st3m/menu.py
+++ /dev/null
@@ -1,400 +0,0 @@
-from st3m import logging
-import math
-
-log = logging.Log(__name__, level=logging.INFO)
-log.info("import")
-
-from . import ui, event
-
-
-import time
-import math
-
-menu_stack = []
-
-
-class Menu:
-    def __init__(self, name="menu", has_back=True):
-        self.name = name
-        self.items = []
-        self.items_petal = [None for i in range(10)]
-        self.__index = 0
-        self.ui = ui.GroupRing(r=80)
-        self.ui2 = ui.GroupPetals(
-            r=100
-        )  # TODO(iggy) hack, this should be composed together in ui
-        self.icon = ui.IconFlower(label=name, size=80)
-        self.ui.element_center = self.icon
-
-        self.angle = 0
-        self.angle_step = 0.2
-
-        if has_back:
-            self.add(MenuItemBack())
-
-    def __repr__(self):
-        return "{} ({}): {}".format(self.name, self.__index, self.items)
-
-    def add(self, item):
-        self.items.append(item)
-        self.ui.add(item.ui)
-
-    def add_petal(self, item, petal_index):
-        self.items_petal[petal_index] = item
-        self.ui2.children[petal_index] = item.ui
-
-    def pop(self):
-        self.items.pop()
-        self.ui.children.pop()
-
-    def start(self):
-        log.info(f"starting menu {self.name}")
-        set_active_menu(self)
-
-    # def scroll(self, n=0):
-    #    self.__index= (self.__index+n)%len(self.items)
-    #    return self.items[self.__index]
-
-    def scroll_app(self, delta):
-        hovered = self.get_hovered_item()
-        if hasattr(hovered, "scroll"):
-            hovered.scroll(delta)
-
-    def scroll_menu(self, delta):
-        if self.angle_step < 0.5:
-            self.angle_step += 0.025
-        self.rotate_steps(delta)
-
-    def enter_menu(self, data={}):
-        hovered = self.get_hovered_item()
-        if hasattr(hovered, "enter_menu"):
-            hovered.enter_menu()
-        else:
-            hovered.enter()
-
-    def enter_app(self, data={}):
-        hovered = self.get_hovered_item()
-        if hasattr(hovered, "enter_app"):
-            hovered.enter_app()
-        else:
-            hovered.enter()
-
-    def rotate_by(self, angle):
-        self.rotate_to(self.angle + angle)
-
-    def rotate_to(self, angle):
-        self.angle = angle % (math.pi * 2)
-        self.ui.angle_offset = self.angle
-        # for child in self.ui.children:
-        #    child.angle_offset = self.angle*2
-
-        self.icon.phi_offset = self.angle
-
-    def rotate_steps(self, steps=1):
-        self.rotate_by(-self.angle_step * steps)
-
-    def _get_hovered_index(self):
-        index = round(-self.angle / (math.pi * 2) * len(self.items))
-        i = index % len(self.items)
-        return i
-
-    def get_hovered_item(self):
-        return self.items[self._get_hovered_index()]
-
-    def _get_angle_for_index(self, index):
-        return (math.pi * 2 / len(self.items) * (index) + self.angle) % (math.pi * 2)
-
-    def _get_topness_for_index(self, index):
-        angle = self._get_angle_for_index(index)
-        dist = min(angle, math.pi * 2 - angle)
-        topness = 1 - (dist / math.pi)
-        return topness
-
-    def draw(self, ctx):
-        # TODO this is more like a hack...
-        # if not self==active_menu:
-        #    active_menu.draw()
-        #    return
-        # print("draw",self.name)
-        hovered_index = self._get_hovered_index()
-        for i in range(len(self.items)):
-            item = self.items[i]
-            my_extra = abs(self._get_topness_for_index(i)) * 40
-
-            if i == hovered_index:
-                item.ui.has_highlight = True
-                my_extra += 20
-            else:
-                item.ui.has_highlight = False
-            item.ui.size = 30 + my_extra
-
-        # black background
-        # TODO transparent menu with compositing
-        ctx.rectangle(-120, -120, 240, 240).rgb(*ui.BLACK).fill()
-
-        self.ui2.draw(ctx)
-        self.ui.draw(ctx)
-
-
-class MenuControl(Menu):
-    def __init__(self, control, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.control = control
-        self.ui.element_center = self.control.ui
-
-    def scroll_app(self, delta):
-        hovered = self.get_hovered_item()
-        if hasattr(hovered, "scroll"):
-            hovered.scroll(delta)
-        else:
-            self.control.scroll(delta)
-
-    def enter_app(self, data={}):
-        hovered = self.get_hovered_item()
-        if hasattr(hovered, "enter_app"):
-            hovered.enter_app()
-        else:
-            self.control.enter()
-
-
-class MenuItem:
-    def __init__(self, name="item", action=None):
-        self.name = name
-        self.action = action
-        self.ui = ui.IconFlower(label=name)
-
-    def __repr__(self):
-        return "item: {} (action: {})".format(self.name, "?")
-
-    def enter(self, data={}):
-        log.info("enter MenuItem {}".format(self.name))
-        if self.action:
-            self.action(data)
-
-
-class MenuItemApp(MenuItem):
-    def __init__(self, app):
-        super().__init__(name=app.title)
-        self.target = app
-
-    def enter(self, data={}):
-        if self.target:
-            submenu_push(None)
-            self.target.run()
-
-
-class MenuItemSubmenu(MenuItem):
-    def __init__(self, submenu):
-        super().__init__(name=submenu.name)
-        self.ui = submenu.icon
-        self.target = submenu
-
-    def enter_menu(self, data={}):
-        log.info("item submenu enter")
-        submenu_push(self.target)
-
-
-class MenuItemBack(MenuItem):
-    def __init__(self):
-        super().__init__(name="")
-        self.ui = ui.IconLabel(label="back")
-
-    def enter_menu(self, data={}):
-        log.info(f"item back selected")
-        menu_back()
-
-
-class MenuItemControl(MenuItem):
-    def __init__(self, name, control):
-        super().__init__(name=name)
-        self.control = control
-        self.ui = control.ui
-
-    def enter_menu(self):
-        log.info(f"item {self.name} (MenuItemControl): enter_menu->enter")
-        self.control.enter()
-
-    def enter_app(self):
-        log.info(f"item {self.name} (MenuItemControl): enter->enter menu")
-        self.control.enter_menu()
-
-    def scroll(self, delta):
-        self.control.scroll(delta)
-
-    def touch_1d(self, x, z):
-        self.control.touch_1d(x, z)
-
-
-def on_scroll(d):
-    active_menu = get_active_menu()
-    if active_menu is None:
-        return
-
-    if d["index"] == 0:  # right button
-        active_menu.scroll_app(d["value"] * 10.0 * d["delta"])
-
-    else:  # index=1, #left button
-        active_menu.scroll_menu(d["value"] * 10.0 * d["delta"])
-
-
-menu_offset = None
-last = time.ticks_ms()
-
-
-def on_scroll_captouch(d):
-    active_menu = get_active_menu()
-    if active_menu is None:
-        return
-    global menu_offset
-    global last
-    # if abs(d["radius"]) < 10000:
-    #    return
-
-    a = math.atan2(-d["radius"] / 600, d["angle"] / 600)
-
-    z = 0
-    if d["change"]:
-        if d["value"] == 1:
-            z = 1
-        else:
-            z = -1
-
-    if z == 1:
-        menu_offset = active_menu.angle - a
-        last = time.ticks_ms()
-    if z == 0:
-        active_menu.rotate_to(menu_offset + a)
-
-    if z == -1:
-        diff = time.ticks_diff(time.ticks_ms(), last)
-        if diff < 300:
-            active_menu.enter_menu()
-
-    # active_menu.rotate_to(a)
-
-
-def on_release(d):
-    active_menu = get_active_menu()
-
-    if active_menu is None:
-        return
-
-    active_menu.angle_step = 0.2
-
-
-def on_touch_1d(d):
-    active_menu = get_active_menu()
-
-    if active_menu is None:
-        return
-
-    v = min(1.0, max(0.0, ((d["radius"] + 25000.0) / 50000.0)))
-    z = 0
-    if d["change"]:
-        if d["value"] == 1:
-            z = 1
-        else:
-            z = -1
-
-    log.debug(f"menu: touch_1d ({v},{z})")
-
-    petal_idx = d["index"]
-    petal_item = active_menu.items_petal[petal_idx]
-    if petal_item:
-        petal_item.touch_1d(v, z)
-
-    if d["index"] == 8:
-        hovered = active_menu.get_hovered_item()
-        if hasattr(hovered, "touch_1d"):
-            hovered.touch_1d(v, z)
-
-
-def on_enter(d):
-    active_menu = get_active_menu()
-
-    # if active_menu is None:
-    #    log.info("menu enter without active menu, opening last menu")
-    #    menu_back()
-    #    return
-    if active_menu:
-        if d["index"] == 0:  # right button
-            log.info("menu enter_app")
-            active_menu.enter_app()
-
-        else:
-            log.info("menu enter_menu")
-            active_menu.enter_menu()
-
-
-event.Event(
-    name="menu rotation button",
-    group_id="menu",
-    condition=lambda e: e["type"] == "button"
-    and not e["change"]
-    and abs(e["value"]) == 1,
-    action=on_scroll,
-    enabled=True,
-)
-
-event.Event(
-    name="menu rotation captouch",
-    group_id="menu",
-    condition=lambda e: e["type"] == "captouch"
-    and (e["value"] == 1 or e["change"])
-    and e["index"] == 2,
-    action=on_scroll_captouch,
-    enabled=True,
-)
-
-
-event.Event(
-    name="menu rotation button release",
-    group_id="menu",
-    condition=lambda e: e["type"] == "button" and e["change"] and e["value"] == 0,
-    action=on_release,
-    enabled=True,
-)
-
-event.Event(
-    name="menu 1d captouch",
-    group_id="menu",
-    condition=lambda e: e["type"] == "captouch" and (e["value"] == 1 or e["change"]),
-    action=on_touch_1d,
-    enabled=True,
-)
-
-event.Event(
-    name="menu button enter",
-    group_id="menu",
-    condition=lambda e: e["type"] == "button" and e["change"] and e["from"] == 2,
-    action=on_enter,
-    enabled=True,
-)
-
-
-def set_active_menu(menu):
-    event.the_engine.active_menu = menu
-
-
-def get_active_menu():
-    return event.the_engine.active_menu
-
-
-def menu_back():
-    previous = menu_stack.pop()
-    log.info(f"back to previous menu {previous.name} (Stack: {len(menu_stack)})")
-
-    set_active_menu(previous)
-
-
-def submenu_push(new_menu):
-    active = get_active_menu()
-    menu_stack.append(active)
-    active.rotate_to(0)
-    if new_menu:
-        log.info(
-            f"enter submenu {new_menu.name} from {active.name} (Stack: {len(menu_stack)})"
-        )
-    else:
-        log.info(f"leaving menu from {active.name}")
-    set_active_menu(new_menu)
diff --git a/python_payload/st3m/ui.py b/python_payload/st3m/ui.py
deleted file mode 100644
index 380d9f0e0d..0000000000
--- a/python_payload/st3m/ui.py
+++ /dev/null
@@ -1,296 +0,0 @@
-from st3m import logging
-
-log = logging.Log(__name__, level=logging.INFO)
-log.info("import")
-
-import random
-import math
-import time
-from math import sin, cos, pi
-
-import gc
-
-WIDTH = 240
-HEIGHT = 240
-
-# Define a few RGB (0.0 to 1.0) colors
-BLACK = (0, 0, 0)
-RED = (1, 0, 0)
-GREEN = (0, 1, 0)
-BLUE = (0, 0, 1)
-WHITE = (1, 1, 1)
-GREY = (0.5, 0.5, 0.5)
-GO_GREEN = (63 / 255, 255 / 255, 33 / 53)
-PUSH_RED = (251 / 255, 72 / 255, 196 / 255)
-
-
-# Utility functions
-def xy_from_polar(r, phi):
-    return (r * math.sin(phi), r * math.cos(phi))  # x  # y
-
-
-# def ctx_circle(self, x,y, radius, arc_from = -math.pi, arc_to = math.pi):
-#    return self.arc(x,y,radius,arc_from,arc_to,True)
-
-# the_ctx.circle = ctx_circle
-
-
-def randrgb():
-    return (random.random(), random.random(), random.random())
-
-
-class UIElement:
-    def __init__(self, origin=(0, 0)):
-        self.children = []
-        self.origin = origin
-
-    def draw(self, ctx, offset=(0, 0)):
-        pos = (self.origin[0] + offset[0], self.origin[1] + offset[1])
-
-        self._draw(ctx, pos)
-        for child in self.children:
-            child.draw(ctx, pos)
-
-    def _draw(self, ctx, pos):
-        pass
-
-    def add(self, child):
-        self.children.append(child)
-
-
-class Viewport(UIElement):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.background = False
-
-    def _draw(self, ctx, pos):
-        if self.background:
-            ctx.rgb(*BLACK).rectangle(-120, -120, 240, 240).fill()
-
-
-class Circle(UIElement):
-    def __init__(
-        self, radius, color=PUSH_RED, arc_from=-math.pi, arc_to=math.pi, *args, **kwargs
-    ):
-        self.radius = radius
-        self.color = color
-        self.arc_from = arc_from
-        self.arc_to = arc_to
-        super().__init__()
-
-    def _draw(self, ctx, pos):
-        (x, y) = pos
-        ctx.move_to(x, y).rgb(*self.color).arc(
-            x, y, self.radius, self.arc_from, self.arc_to, True
-        ).fill()
-
-
-class Text(UIElement):
-    def __init__(self, s="foo"):
-        self.s = s
-        super().__init__()
-
-    def _draw(self, ctx, pos):
-        ctx.text_align = ctx.CENTER
-        ctx.text_baseline = ctx.MIDDLE
-        ctx.font_size = 30
-        ctx.rgb(1, 1, 1).move_to(pos[0], pos[1]).text(self.s)
-
-
-class Icon(UIElement):
-    def __init__(self, label="?", size=60):
-        self.bg = (random.random(), random.random(), random.random())
-        self.fg = 0
-        self.label = label
-        self.size = size
-        self.has_highlight = False
-        super().__init__()
-
-    def _draw(self, ctx, pos):
-        ctx.text_align = ctx.CENTER
-        ctx.text_baseline = ctx.MIDDLE
-        ctx.font_size = self.size / 3
-
-        (x, y) = pos
-        hs = 5
-
-        if self.has_highlight:
-            ctx.rgb(*GO_GREEN).arc(
-                x, y, self.size / 2 + hs, -math.pi, math.pi, True
-            ).fill()
-        ctx.move_to(x, y).rgb(*self.bg).arc(
-            x, y, self.size / 2, -math.pi, math.pi, True
-        ).fill()
-
-        y += self.size / 3
-        width = max(self.size - 10, ctx.text_width(self.label)) + 10
-        height = self.size / 3 + 8
-        if self.has_highlight:
-            ctx.rgb(*BLACK).move_to(x, y - height / 2).round_rectangle(
-                x - width / 2, y - height / 2, width, height, width // 2
-            ).fill()
-            ctx.rgb(*GO_GREEN).move_to(x, y).text(self.label)
-        else:
-            ctx.rgb(*PUSH_RED).move_to(x, y - height / 2).round_rectangle(
-                x - width / 2, y - height / 2, width, height, width // 2
-            ).fill()
-            ctx.rgb(*BLACK).move_to(x, y).text(self.label)
-
-
-class IconLabel(Icon):
-    def _draw(self, ctx, pos):
-        ctx.text_align = ctx.CENTER
-        ctx.text_baseline = ctx.MIDDLE
-        ctx.font_size = self.size / 2
-
-        (x, y) = pos
-        hs = 5
-        width = ctx.text_width(self.label) + 10
-        height = self.size / 2
-        if self.has_highlight:
-            ctx.rgb(*GO_GREEN).move_to(x, y - height / 2).round_rectangle(
-                x - width / 2, y - height / 2, width, height, width // 2
-            ).fill()
-            ctx.rgb(*BLACK).move_to(x, y).text(self.label)
-        else:
-            ctx.rgb(*PUSH_RED).move_to(x, y - height / 2).round_rectangle(
-                x - width / 2, y - height / 2, width, height, width // 2
-            ).fill()
-            ctx.rgb(*BLACK).move_to(x, y).text(self.label)
-
-
-class IconFlower(Icon):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.petal_count = random.randint(3, 5)
-        # self.petal_count = 0
-        self.petal_color = (random.random(), random.random(), random.random())
-        self.phi_offset = random.random()
-        self.size_offset = random.randint(0, 20)
-        # self.bg=PUSH_RED
-
-    def _draw(self, ctx, pos):
-        ctx.text_align = ctx.CENTER
-        ctx.text_baseline = ctx.MIDDLE
-        ctx.font_size = self.size / 3
-
-        (x, y) = pos
-        petal_size = 0
-        if self.petal_count:
-            petal_size = 2.3 * self.size / self.petal_count + self.size_offset
-
-        hs = 5
-
-        for i in range(self.petal_count):
-            phi = math.pi * 2 / self.petal_count * i + self.phi_offset
-            r = self.size / 2
-            (x_, y_) = xy_from_polar(r, phi)
-            size_rnd = random.randint(-1, 1)
-            if self.has_highlight:
-                ctx.move_to(x + x_, y + y_).rgb(*GO_GREEN).arc(
-                    x + x_,
-                    y + y_,
-                    petal_size / 2 + hs + size_rnd,
-                    -math.pi,
-                    math.pi,
-                    True,
-                ).fill()
-            ctx.move_to(x + x_, y + y_).rgb(*self.petal_color).arc(
-                x + x_, y + y_, petal_size / 2 + size_rnd, -math.pi, math.pi, True
-            ).fill()
-
-        if self.has_highlight:
-            ctx.rgb(*GO_GREEN).arc(
-                x, y, self.size / 2 + hs, -math.pi, math.pi, True
-            ).fill()
-        ctx.move_to(x, y).rgb(*self.bg).arc(
-            x, y, self.size / 2, -math.pi, math.pi, True
-        ).fill()
-
-        y += self.size / 3
-        width = max(self.size, ctx.text_width(self.label) + 10)
-        height = self.size / 3 + 8
-        if self.has_highlight:
-            ctx.rgb(*BLACK).move_to(x, y - height / 2).round_rectangle(
-                x - width / 2, y - height / 2, width, height, width // 2
-            ).fill()
-            ctx.rgb(*GO_GREEN).move_to(x, y).text(self.label)
-        else:
-            ctx.rgb(*PUSH_RED).move_to(x, y - height / 2).round_rectangle(
-                x - width / 2, y - height / 2, width, height, width // 2
-            ).fill()
-            ctx.rgb(*BLACK).move_to(x, y).text(self.label)
-
-
-class IconValue(Icon):
-    def __init__(self, value=0, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.value = value
-        self.get_value = None
-
-    def _draw(self, ctx, pos):
-        (x, y) = pos
-
-        v = self.value
-        if self.get_value:
-            v = self.get_value()
-            self.value = v
-
-        ctx.text_align = ctx.CENTER
-        ctx.text_baseline = ctx.MIDDLE
-        ctx.font_size = self.size / 3
-
-        if self.has_highlight:
-            ctx.move_to(x, y).rgb(*WHITE).arc(
-                x, y, self.size / 2 + 5, -pi, pi, True
-            ).fill()
-
-        ctx.move_to(x, y).rgb(*PUSH_RED).arc(x, y, self.size / 2, -pi, pi, True).fill()
-        ctx.move_to(x, y).rgb(*GO_GREEN).arc(
-            x, y, self.size / 2 - 5, v * 2 * pi, 0, 1
-        ).fill()
-        ctx.rgb(0, 0, 0).move_to(x, y).text(self.label)
-
-        ctx.move_to(x, y).rgb(*PUSH_RED).arc(x, y, self.size / 2, -pi, pi, True).fill()
-        ctx.move_to(x, y).rgb(*GO_GREEN).arc(
-            x, y, self.size / 2 - 5, v * 2 * pi, 0, 1
-        ).fill()
-        ctx.rgb(0, 0, 0).move_to(x, y).text(self.label)
-
-
-class GroupStackedVertical(UIElement):
-    pass
-
-
-class GroupRing(UIElement):
-    def __init__(self, r=100, origin=(0, 0), element_center=None):
-        self.r = r
-        self.angle_offset = 0
-        self.element_center = element_center
-        super().__init__(origin)
-
-    def draw(self, ctx, offset=(0, 0)):
-        pos = (self.origin[0] + offset[0], self.origin[1] + offset[1])
-        self._draw(ctx, pos)
-        for index in range(len(self.children)):
-            # print("child",index)
-            child = self.children[index]
-            if not child:
-                continue
-            angle = 2 * math.pi / len(self.children) * index + self.angle_offset
-            # print(angle,self.r,pos[0])
-            x = -math.sin(angle) * self.r + pos[0]
-            y = -math.cos(angle) * self.r + pos[1]
-            # print("pos",(x,y))
-            child.draw(ctx, offset=(x, y))
-
-    def _draw(self, ctx, pos):
-        if self.element_center:
-            self.element_center.has_highlight = False
-            self.element_center._draw(ctx, pos)
-
-
-class GroupPetals(GroupRing):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.children = [None for i in range(10)]
diff --git a/python_payload/st3m/utils.py b/python_payload/st3m/utils.py
deleted file mode 100644
index 6ba9eb4817..0000000000
--- a/python_payload/st3m/utils.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from st3m import logging
-
-log = logging.Log(__name__, level=logging.INFO)
-log.info("import")
-
-import time
-from hardware import *
-from st3m.system import audio
-
-RED = 0b1111100000000000
-GREEN = 0b0000011111100000
-BLUE = 0b0000000000011111
-
-
-def clear_all_leds():
-    for i in range(40):
-        set_led_rgb(i, 0, 0, 0)
-    update_leds()
-
-
-def highlight_bottom_petal(num, RED, GREEN, BLUE):
-    start = 4 + 8 * num
-    for i in range(7):
-        set_led_rgb(((i + start) % 40), RED, GREEN, BLUE)
-    update_leds()
-
-
-def long_bottom_petal_captouch_blocking(num, ms):
-    if get_captouch((num * 2) + 1) == 1:
-        time.sleep_ms(ms)
-        if get_captouch((num * 2) + 1) == 1:
-            return True
-    return False
-
-
-def draw_volume_slider(ctx, volume):
-    length = int(96 * volume)
-
-    ctx.rgb(0, 0, 0)  # dummy
-    ctx.round_rectangle(-49, 41, 98, 8, 3).fill()  # dummy idk
-
-    ctx.rgb(0, 255, 0)
-    ctx.round_rectangle(-51, 49, 102, 12, 3).fill()
-    ctx.rgb(0, 0, 0)
-    ctx.round_rectangle(-50, 50, 100, 10, 3).fill()
-    ctx.rgb(0, 255, 0)
-    ctx.round_rectangle(-48, 52, length, 6, 3).fill()
-- 
GitLab