소스 검색

down to 2 seconds

Jari 3 달 전
부모
커밋
fc1258dd2d
1개의 변경된 파일과 47개의 추가작업 그리고 24개의 파일을 삭제
  1. 47 24
      ReversePuck/ui.py

+ 47 - 24
ReversePuck/ui.py

@@ -3,16 +3,19 @@
 Big tappable tiles, one per bonded puck. A tile is tappable when its puck is LIVE (the RF link is up).
 Tapping a live tile detaches the Deck controls and forwards to that puck (a single tap -- connecting is
 cheap and reversible). The two DESTRUCTIVE actions -- stopping/disconnecting a forward, and removing a
-bond (unpair) -- instead require a 5-SECOND HOLD: an errant screen tap mid-game used to drop the forward
+bond (unpair) -- instead require a HOLD (HOLD_SECS): an errant screen tap mid-game used to drop the forward
 (which on the puck side tears down a USB device and could reboot the MCU), so a brief touch must no longer
 trigger it. A progress fill shows the hold; lifting before it completes cancels. Sized for the Deck's
 1280x800 touchscreen but scales.
+
+Status icons (dots, the remove ✕) are drawn with primitives, not font glyphs -- the Deck's available fonts
+don't carry ● ○ ✕ etc., so rendering them as text just showed blank/boxes.
 """
 import time
 import pygame
 
 # Destructive actions (stop forwarding, remove bond) require holding this long; a tap no longer triggers them.
-HOLD_SECS = 5.0
+HOLD_SECS = 2.0
 
 BG = (16, 18, 24)
 FG = (230, 232, 238)
@@ -31,6 +34,23 @@ def _font(sz, bold=False):
     return f
 
 
+def _draw_dot(screen, center, r, color, filled=True):
+    """Status dot drawn with primitives -- font glyphs (● ○) don't render on the Deck's fallback font."""
+    if filled:
+        pygame.draw.circle(screen, color, center, r)
+    else:
+        pygame.draw.circle(screen, color, center, r, max(2, r // 3))
+
+
+def _draw_cross(screen, rect, color, width):
+    """Remove-bond ✕ drawn as two diagonals (the font glyph doesn't render)."""
+    pad = max(6, rect.width // 4)
+    pygame.draw.line(screen, color, (rect.x + pad, rect.y + pad),
+                     (rect.right - pad, rect.bottom - pad), width)
+    pygame.draw.line(screen, color, (rect.right - pad, rect.y + pad),
+                     (rect.x + pad, rect.bottom - pad), width)
+
+
 def run(app):
     pygame.init()
     pygame.display.set_caption("ReversePuck · Steam Deck")
@@ -59,15 +79,22 @@ def run(app):
         screen.fill(BG)
         s = app.status
         connected = app.dongle_connected
-        # compact status line (no big title banner): dongle presence first, then RF link
+        # compact status line (no big title banner): dongle presence first, then RF link. The leading status
+        # dot is drawn (font glyphs don't render); filled = linked, ring = no-dongle / searching.
         if not connected:
-            badge, col = "⨯ no dongle", WARN
+            badge, col = "no dongle", WARN
         elif s["link_up"]:
-            badge, col = "● linked  ch%d" % s["sess_ch"], LIVE
+            badge, col = "linked  ch%d" % s["sess_ch"], LIVE
         else:
-            badge, col = "○ searching…", DIM
+            badge, col = "searching...", DIM
         t = f_med.render(badge, True, col)
-        screen.blit(t, (W - t.get_width() - 40, 24))
+        bx = W - t.get_width() - 40
+        screen.blit(t, (bx, 24))
+        _draw_dot(screen, (bx - 18, 24 + t.get_height() // 2), 8, col,
+                  filled=connected and s["link_up"])
+        # legend (top-left): how the gestures work
+        screen.blit(f_small.render("Tap to connect, hold to disconnect or remove", True, DIM),
+                    (40, 30))
 
         # tiles -- live pucks float to the top, then by slot index
         tile_rects = []
@@ -80,7 +107,7 @@ def run(app):
         if not connected:
             msg = f_big.render("Plug in the ReversePuck dongle", True, WARN)
             screen.blit(msg, (W // 2 - msg.get_width() // 2, H // 2 - 40))
-            sub = f_small.render("Waiting for the nRF (Valve 28DE:1302) on USB… it'll appear here automatically.",
+            sub = f_small.render("Waiting for the nRF (Valve 28DE:1302) on USB... it'll appear here automatically.",
                                  True, DIM)
             screen.blit(sub, (W // 2 - sub.get_width() // 2, H // 2 + 20))
         elif not bonds:
@@ -98,17 +125,16 @@ def run(app):
             color = TILE_SEL if sel else (TILE_LIVE if b["alive"] else TILE)
             pygame.draw.rect(screen, color, rect, border_radius=18)
             pygame.draw.rect(screen, (ACCENT if (tappable or sel) else DIM), rect, width=2, border_radius=18)
-            # glyph + serial
-            glyph = "●" if b["alive"] else "○"
+            # status dot (drawn -- font ● ○ don't render) + serial
             gcol = LIVE if b["alive"] else DIM
-            screen.blit(f_big.render(glyph, True, gcol), (rect.x + 30, rect.y + th // 2 - 22))
+            _draw_dot(screen, (rect.x + 46, rect.y + th // 2), 13, gcol, filled=b["alive"])
             screen.blit(f_big.render(b["serial"] or "puck %d" % slot, True, FG),
                         (rect.x + 90, rect.y + 20))
-            state = ("FORWARDING — hold anywhere to stop" if sel
-                     else "available — tap to forward" if tappable
+            state = ("FORWARDING - hold anywhere to stop" if sel
+                     else "available - tap to forward" if tappable
                      else "live (forwarding elsewhere)" if b["alive"]
-                     else "paired · offline")
-            screen.blit(f_small.render("slot %d · %s" % (slot, state), True, DIM),
+                     else "paired - offline")
+            screen.blit(f_small.render("slot %d - %s" % (slot, state), True, DIM),
                         (rect.x + 90, rect.y + th - 38))
             tile_rects.append((rect, slot, tappable or sel))
             # remove-bond ✕ on the far right (only when idle -- can't un-bond while forwarding)
@@ -117,18 +143,15 @@ def run(app):
                 drect = pygame.Rect(rect.right - dsz - 18, rect.y + (th - dsz) // 2, dsz, dsz)
                 pygame.draw.rect(screen, TILE, drect, border_radius=12)
                 # while this ✕ is being held, fill it from the bottom up as a hold-to-unpair progress meter
+                held = (hold is not None and hold["kind"] == "unpair"
+                        and hold["slot"] == slot)
                 hp = hold_progress()
-                if hold is not None and hold["kind"] == "unpair" and hold["slot"] == slot and hp:
+                if held and hp:
                     fh = int(dsz * hp)
                     fill = pygame.Rect(drect.x, drect.bottom - fh, dsz, fh)
                     pygame.draw.rect(screen, STOP, fill, border_radius=12)
                 pygame.draw.rect(screen, STOP, drect, width=2, border_radius=12)
-                x = f_big.render("✕", True, FG if (hold is not None and hold["kind"] == "unpair"
-                                                   and hold["slot"] == slot) else STOP)
-                screen.blit(x, (drect.centerx - x.get_width() // 2,
-                                drect.centery - x.get_height() // 2))
-                screen.blit(f_small.render("hold to remove", True, DIM),
-                            (drect.x - 6, drect.bottom + 2))
+                _draw_cross(screen, drect, FG if held else STOP, 4)
                 del_rects.append((drect, slot))
 
         # firmware log panel (the in-app serial monitor) — fills the area below the tiles
@@ -159,10 +182,10 @@ def run(app):
                 pygame.draw.rect(screen, (255, 150, 150),
                                  pygame.Rect(bar.x, bar.y, int(bar.width * hp), bar.height))
                 left = max(0.0, HOLD_SECS * (1.0 - hp))
-                msg = f_big.render("KEEP HOLDING TO STOP — %.0fs" % (left + 0.99),
+                msg = f_big.render("KEEP HOLDING TO STOP - %.0fs" % (left + 0.99),
                                    True, (255, 255, 255))
             else:
-                msg = f_big.render("FORWARDING — Deck controls detached · hold anywhere 5s to stop",
+                msg = f_big.render("FORWARDING - Deck controls detached - hold to stop",
                                    True, (255, 255, 255))
             screen.blit(msg, (W // 2 - msg.get_width() // 2, bar.y + bar.height // 2 - 22))
         pygame.display.flip()