feat(oled): strict K0/K1 nav + chord reboot + arrow chrome + controller cycle
Refactor the Pico-OLED-1.3 button model and visual layout:
- KEY0 single press = "next screen" (double-click reboot path removed —
it was easy to fire by accident while paging quickly).
- KEY1 short press = "previous screen" on every screen. The old
contextual cycle on Trigger Test (cycle trigger preset) and Lightbar
(cycle lb_mode) moves to controller buttons:
* △ rising edge on Trigger Test → cycle trigger preset
(re-applies via send_trigger_effect)
* R1 rising edge on Lightbar → cycle lb_mode
△ on Lightbar stays as "save current RGB to favorite slot 0" (existing
UX), so R1 is used there instead.
- KEY1 long press = cycle brightness — unchanged.
- KEY0 + KEY1 held simultaneously for >= 1 s → watchdog_reboot. Pre-empts
per-key handlers so a chord cancels any armed single press. DS5
PS+Mute hold-2 s remains the headless backup.
Visual layout:
- New draw_button_chrome() paints '>' at (0, 8) and '<' at (0, 49) on
every frame from flush_fb(), pairing the on-screen labels with the
physical KEY0/KEY1 buttons on the left edge of the device.
- Every render_screen_*() shifts main content right by kContentX (6 px)
to clear the chrome strip. Stick boxes, L2 column, L1 bar, and D-pad
center on the Status screen also shift right by 6 to avoid the '<'
glyph painting inside the live left-stick area.
- "K0=next K1=back" footers removed from all screens (the chrome strip
is now the affordance). Trigger Test footer = "Tri=cycle"; Lightbar
footer = "R1=mode"; Slots/Settings keep their contextual hints.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
c5759dfce8
commit
3eb0e76508
+181
-112
@@ -42,15 +42,21 @@ uint32_t key0_t_us = 0;
|
|||||||
uint32_t key1_t_us = 0;
|
uint32_t key1_t_us = 0;
|
||||||
constexpr uint32_t kDebounceUs = 20000;
|
constexpr uint32_t kDebounceUs = 20000;
|
||||||
|
|
||||||
uint32_t key0_first_press_us = 0;
|
// Single-press latch — armed on rising edge, fired on release. KEY0 was
|
||||||
bool key0_pending_single = false;
|
// previously a double-click reboot trigger; that gesture moved to the
|
||||||
constexpr uint32_t kDoubleClickUs = 400000;
|
// KEY0+KEY1 chord below because rapid forward-navigation kept tripping it.
|
||||||
|
bool key0_armed = false;
|
||||||
|
|
||||||
// KEY1 long-press detection (for brightness cycling)
|
// KEY1 long-press detection (for brightness cycling)
|
||||||
uint32_t key1_press_us = 0;
|
uint32_t key1_press_us = 0;
|
||||||
bool key1_was_pressed = false;
|
bool key1_was_pressed = false;
|
||||||
constexpr uint32_t kLongPressUs = 1500000;
|
constexpr uint32_t kLongPressUs = 1500000;
|
||||||
|
|
||||||
|
// KEY0 + KEY1 simultaneous hold → watchdog_reboot. 1 s hold is long enough
|
||||||
|
// to filter accidental two-button taps but short enough to feel responsive.
|
||||||
|
uint32_t chord_held_since_us = 0;
|
||||||
|
constexpr uint32_t kChordHoldUs = 1000000;
|
||||||
|
|
||||||
// Brightness levels (SH1107 contrast register 0x81). User cycles via KEY1 long-press.
|
// Brightness levels (SH1107 contrast register 0x81). User cycles via KEY1 long-press.
|
||||||
constexpr uint8_t kBrightLevels[] = {0xFF, 0x7F, 0x3F, 0x10};
|
constexpr uint8_t kBrightLevels[] = {0xFF, 0x7F, 0x3F, 0x10};
|
||||||
constexpr int kNumBrightLevels = sizeof(kBrightLevels) / sizeof(kBrightLevels[0]);
|
constexpr int kNumBrightLevels = sizeof(kBrightLevels) / sizeof(kBrightLevels[0]);
|
||||||
@@ -119,6 +125,13 @@ constexpr uint32_t kRumbleBurstUs = 250000;
|
|||||||
|
|
||||||
int trigger_preset = 0;
|
int trigger_preset = 0;
|
||||||
const char* const kTrigPresetNames[] = {"Off", "Feedback", "Weapon", "Vibration", "Bow", "Gallop", "Machine"};
|
const char* const kTrigPresetNames[] = {"Off", "Feedback", "Weapon", "Vibration", "Bow", "Gallop", "Machine"};
|
||||||
|
|
||||||
|
// Rising-edge trackers for the screens whose K1=cycle action moved to a
|
||||||
|
// controller button. Trigger Test uses △ (byte 7 bit 7); Lightbar uses R1
|
||||||
|
// (byte 8 bit 1) because △ is already taken on Lightbar for "save current
|
||||||
|
// RGB to favorite slot 0".
|
||||||
|
uint8_t triggers_last_face = 0;
|
||||||
|
uint8_t lb_last_buttons = 0;
|
||||||
constexpr int kNumTrigPresets = 7;
|
constexpr int kNumTrigPresets = 7;
|
||||||
|
|
||||||
void cmd(uint8_t c) {
|
void cmd(uint8_t c) {
|
||||||
@@ -175,7 +188,13 @@ void sh1107_init() {
|
|||||||
cmd(0xAF);
|
cmd(0xAF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forward-declared so flush_fb can paint the per-button arrows on top of
|
||||||
|
// the rendered framebuffer just before SPI sends it to the OLED. Body
|
||||||
|
// lives near the other text-drawing helpers below.
|
||||||
|
void draw_button_chrome();
|
||||||
|
|
||||||
void flush_fb() {
|
void flush_fb() {
|
||||||
|
draw_button_chrome();
|
||||||
cmd(0xB0);
|
cmd(0xB0);
|
||||||
for (int j = 0; j < kH; j++) {
|
for (int j = 0; j < kH; j++) {
|
||||||
const uint8_t col = kH - 1 - j;
|
const uint8_t col = kH - 1 - j;
|
||||||
@@ -225,6 +244,17 @@ void draw_text(int x, int y, const char *s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Button-chrome strip on the left edge of every screen. KEY0 (top button)
|
||||||
|
// shows '>' at y=8; KEY1 (bottom button) shows '<' at y=49. Painted by
|
||||||
|
// flush_fb() on top of the rendered framebuffer so it never gets clobbered.
|
||||||
|
// Per-screen renderers reserve x ∈ [0..5] (5-wide glyph + 1 padding) and
|
||||||
|
// start main content at kContentX.
|
||||||
|
constexpr int kContentX = 6;
|
||||||
|
void draw_button_chrome() {
|
||||||
|
draw_char(0, 8, '>');
|
||||||
|
draw_char(0, 49, '<');
|
||||||
|
}
|
||||||
|
|
||||||
// Pixel-art icon support. Visual approach inspired by zurce/DS5Dongle-OLED
|
// Pixel-art icon support. Visual approach inspired by zurce/DS5Dongle-OLED
|
||||||
// (https://github.com/zurce/DS5Dongle-OLED) — credit to zurce for the idea
|
// (https://github.com/zurce/DS5Dongle-OLED) — credit to zurce for the idea
|
||||||
// of decorating the OLED with small bitmaps instead of bare text/shapes.
|
// of decorating the OLED with small bitmaps instead of bare text/shapes.
|
||||||
@@ -387,22 +417,41 @@ void handle_buttons() {
|
|||||||
const uint32_t now = time_us_32();
|
const uint32_t now = time_us_32();
|
||||||
const bool k0 = gpio_get(kPinKey0);
|
const bool k0 = gpio_get(kPinKey0);
|
||||||
const bool k1 = gpio_get(kPinKey1);
|
const bool k1 = gpio_get(kPinKey1);
|
||||||
|
|
||||||
|
// KEY0 + KEY1 chord — both held >= kChordHoldUs triggers watchdog_reboot.
|
||||||
|
// Pre-empts the per-key handlers so a chord cancels any armed single
|
||||||
|
// press (whichever key gets released first won't also navigate).
|
||||||
|
const bool chord = !k0 && !k1;
|
||||||
|
if (chord) {
|
||||||
|
if (chord_held_since_us == 0) chord_held_since_us = now;
|
||||||
|
key0_armed = false;
|
||||||
|
key1_was_pressed = false;
|
||||||
|
if ((now - chord_held_since_us) >= kChordHoldUs) {
|
||||||
|
watchdog_reboot(0, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chord_held_since_us = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// KEY0: arm on debounced rising edge, fire "next screen" on release.
|
||||||
|
// Releasing without a chord during the hold = pure forward-nav.
|
||||||
if (!k0 && key0_prev && (now - key0_t_us) > kDebounceUs) {
|
if (!k0 && key0_prev && (now - key0_t_us) > kDebounceUs) {
|
||||||
key0_t_us = now;
|
key0_t_us = now;
|
||||||
if (key0_pending_single && (now - key0_first_press_us) < kDoubleClickUs) {
|
key0_armed = true;
|
||||||
key0_pending_single = false;
|
last_activity_us = now;
|
||||||
watchdog_reboot(0, 0, 0);
|
|
||||||
} else {
|
|
||||||
key0_pending_single = true;
|
|
||||||
key0_first_press_us = now;
|
|
||||||
}
|
}
|
||||||
}
|
if (k0 && !key0_prev && key0_armed) {
|
||||||
if (key0_pending_single && (now - key0_first_press_us) > kDoubleClickUs) {
|
key0_armed = false;
|
||||||
key0_pending_single = false;
|
|
||||||
current_screen = (current_screen + 1) % kNumScreens;
|
current_screen = (current_screen + 1) % kNumScreens;
|
||||||
last_render_us = 0;
|
last_render_us = 0;
|
||||||
|
last_activity_us = now;
|
||||||
}
|
}
|
||||||
// KEY1: track press time, decide on release whether it was short or long
|
|
||||||
|
// KEY1: arm on press, fire on release. Short press = back; long press
|
||||||
|
// = brightness cycle (unchanged). Trigger-preset / lightbar-mode cycle
|
||||||
|
// moved to the DualSense △ button — see triggers_handle_input() and
|
||||||
|
// lightbar_handle_input(). The chord above clears key1_was_pressed so
|
||||||
|
// a released-after-chord K1 doesn't navigate back.
|
||||||
if (!k1 && key1_prev && (now - key1_t_us) > kDebounceUs) {
|
if (!k1 && key1_prev && (now - key1_t_us) > kDebounceUs) {
|
||||||
key1_t_us = now;
|
key1_t_us = now;
|
||||||
key1_press_us = now;
|
key1_press_us = now;
|
||||||
@@ -414,24 +463,13 @@ void handle_buttons() {
|
|||||||
const uint32_t held = now - key1_press_us;
|
const uint32_t held = now - key1_press_us;
|
||||||
last_activity_us = now;
|
last_activity_us = now;
|
||||||
if (held > kLongPressUs) {
|
if (held > kLongPressUs) {
|
||||||
// Long press: cycle brightness level
|
|
||||||
bright_idx = (bright_idx + 1) % kNumBrightLevels;
|
bright_idx = (bright_idx + 1) % kNumBrightLevels;
|
||||||
} else {
|
|
||||||
// Short press: contextual per screen. On Trigger Test / Lightbar
|
|
||||||
// KEY1 stays as the primary in-screen cycle. Everywhere else it
|
|
||||||
// acts as a "back" button — cycles to the previous screen so
|
|
||||||
// KEY0/KEY1 form a natural forward/back pair.
|
|
||||||
if (current_screen == kScreenTriggers) {
|
|
||||||
trigger_preset = (trigger_preset + 1) % kNumTrigPresets;
|
|
||||||
send_trigger_effect(trigger_preset);
|
|
||||||
} else if (current_screen == kScreenLightbar) {
|
|
||||||
lb_mode = (lb_mode + 1) % kNumLbModes;
|
|
||||||
} else {
|
} else {
|
||||||
current_screen = (current_screen - 1 + kNumScreens) % kNumScreens;
|
current_screen = (current_screen - 1 + kNumScreens) % kNumScreens;
|
||||||
last_render_us = 0;
|
last_render_us = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
key0_prev = k0;
|
key0_prev = k0;
|
||||||
key1_prev = k1;
|
key1_prev = k1;
|
||||||
}
|
}
|
||||||
@@ -441,7 +479,7 @@ __attribute__((noinline)) void render_screen() {
|
|||||||
|
|
||||||
const bool connected = bt_is_connected();
|
const bool connected = bt_is_connected();
|
||||||
|
|
||||||
draw_text(0, 0, "DS5 Bridge v0.6.0");
|
draw_text(kContentX, 0, "DS5 Bridge v0.6.0");
|
||||||
draw_icon(120, 0, connected ? kIconLinkOn : kIconLinkOff, 8, 8);
|
draw_icon(120, 0, connected ? kIconLinkOn : kIconLinkOff, 8, 8);
|
||||||
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
@@ -450,7 +488,7 @@ __attribute__((noinline)) void render_screen() {
|
|||||||
char buf[24];
|
char buf[24];
|
||||||
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
|
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||||
a[0], a[1], a[2], a[3], a[4], a[5]);
|
a[0], a[1], a[2], a[3], a[4], a[5]);
|
||||||
draw_text(0, 9, buf);
|
draw_text(kContentX, 9, buf);
|
||||||
|
|
||||||
const uint8_t pwr = interrupt_in_data[52];
|
const uint8_t pwr = interrupt_in_data[52];
|
||||||
int pct = (pwr & 0x0F) * 10;
|
int pct = (pwr & 0x0F) * 10;
|
||||||
@@ -462,11 +500,13 @@ __attribute__((noinline)) void render_screen() {
|
|||||||
else if (pstate >= 0xA) marker = '!'; // Error
|
else if (pstate >= 0xA) marker = '!'; // Error
|
||||||
char bbuf[16];
|
char bbuf[16];
|
||||||
snprintf(bbuf, sizeof(bbuf), "%3d%%%c", pct, marker);
|
snprintf(bbuf, sizeof(bbuf), "%3d%%%c", pct, marker);
|
||||||
draw_text(0, 18, bbuf);
|
draw_text(kContentX, 18, bbuf);
|
||||||
draw_battery_icon(30, 18, pct);
|
draw_battery_icon(36, 18, pct);
|
||||||
|
|
||||||
rect_outline(0, 30, 32, 32);
|
// Left-half visuals are shifted right by kContentX so the < button
|
||||||
int lx = 2 + (interrupt_in_data[0] * 27) / 255;
|
// chrome at (x=0, y=49) doesn't paint over the live stick dot.
|
||||||
|
rect_outline(kContentX, 30, 32, 32);
|
||||||
|
int lx = (kContentX + 2) + (interrupt_in_data[0] * 27) / 255;
|
||||||
int ly = 32 + (interrupt_in_data[1] * 27) / 255;
|
int ly = 32 + (interrupt_in_data[1] * 27) / 255;
|
||||||
rect_filled(lx - 1, ly - 1, 3, 3);
|
rect_filled(lx - 1, ly - 1, 3, 3);
|
||||||
|
|
||||||
@@ -475,10 +515,11 @@ __attribute__((noinline)) void render_screen() {
|
|||||||
int ry = 32 + (interrupt_in_data[3] * 27) / 255;
|
int ry = 32 + (interrupt_in_data[3] * 27) / 255;
|
||||||
rect_filled(rx - 1, ry - 1, 3, 3);
|
rect_filled(rx - 1, ry - 1, 3, 3);
|
||||||
|
|
||||||
// L2/R2 analog trigger bars (vertical, fill from bottom)
|
// L2/R2 analog trigger bars (vertical, fill from bottom). L2 sits
|
||||||
rect_outline(32, 33, 4, 29);
|
// just right of the shifted left stick box.
|
||||||
|
rect_outline(kContentX + 32, 33, 4, 29);
|
||||||
const int l2_fill = (interrupt_in_data[4] * 27) / 255;
|
const int l2_fill = (interrupt_in_data[4] * 27) / 255;
|
||||||
if (l2_fill > 0) rect_filled(33, 61 - l2_fill, 2, l2_fill);
|
if (l2_fill > 0) rect_filled(kContentX + 33, 61 - l2_fill, 2, l2_fill);
|
||||||
rect_outline(92, 33, 4, 29);
|
rect_outline(92, 33, 4, 29);
|
||||||
const int r2_fill = (interrupt_in_data[5] * 27) / 255;
|
const int r2_fill = (interrupt_in_data[5] * 27) / 255;
|
||||||
if (r2_fill > 0) rect_filled(93, 61 - r2_fill, 2, r2_fill);
|
if (r2_fill > 0) rect_filled(93, 61 - r2_fill, 2, r2_fill);
|
||||||
@@ -486,13 +527,14 @@ __attribute__((noinline)) void render_screen() {
|
|||||||
const uint8_t b7 = interrupt_in_data[7];
|
const uint8_t b7 = interrupt_in_data[7];
|
||||||
const uint8_t b8 = interrupt_in_data[8];
|
const uint8_t b8 = interrupt_in_data[8];
|
||||||
|
|
||||||
// D-pad indicator (4 directions; lit for primary + diagonals)
|
// D-pad indicator (4 directions; lit for primary + diagonals).
|
||||||
|
// Centered between the left stick column and the face-button cluster.
|
||||||
const int dp = b7 & 0x0F;
|
const int dp = b7 & 0x0F;
|
||||||
const bool dp_n = (dp == 7 || dp == 0 || dp == 1);
|
const bool dp_n = (dp == 7 || dp == 0 || dp == 1);
|
||||||
const bool dp_e = (dp == 1 || dp == 2 || dp == 3);
|
const bool dp_e = (dp == 1 || dp == 2 || dp == 3);
|
||||||
const bool dp_s = (dp == 3 || dp == 4 || dp == 5);
|
const bool dp_s = (dp == 3 || dp == 4 || dp == 5);
|
||||||
const bool dp_w = (dp == 5 || dp == 6 || dp == 7);
|
const bool dp_w = (dp == 5 || dp == 6 || dp == 7);
|
||||||
const int dcx = 46, dcy = 46;
|
const int dcx = 52, dcy = 46;
|
||||||
auto dot = [&](int dx, int dy, bool on) {
|
auto dot = [&](int dx, int dy, bool on) {
|
||||||
if (on) rect_filled(dcx + dx - 2, dcy + dy - 2, 5, 5);
|
if (on) rect_filled(dcx + dx - 2, dcy + dy - 2, 5, 5);
|
||||||
else rect_outline(dcx + dx - 2, dcy + dy - 2, 5, 5);
|
else rect_outline(dcx + dx - 2, dcy + dy - 2, 5, 5);
|
||||||
@@ -514,13 +556,14 @@ __attribute__((noinline)) void render_screen() {
|
|||||||
sq(fcx_off + 0, 8, b7 & 0x20); // Cross
|
sq(fcx_off + 0, 8, b7 & 0x20); // Cross
|
||||||
sq(fcx_off - 8, 0, b7 & 0x10); // Square
|
sq(fcx_off - 8, 0, b7 & 0x10); // Square
|
||||||
|
|
||||||
if (b8 & 0x01) rect_filled(36, 30, 12, 3); else rect_outline(36, 30, 12, 3); // L1
|
// L1 bar shifted to sit between the L2 trigger column and the d-pad.
|
||||||
|
if (b8 & 0x01) rect_filled(42, 30, 8, 3); else rect_outline(42, 30, 8, 3); // L1
|
||||||
if (b8 & 0x02) rect_filled(80, 30, 12, 3); else rect_outline(80, 30, 12, 3); // R1
|
if (b8 & 0x02) rect_filled(80, 30, 12, 3); else rect_outline(80, 30, 12, 3); // R1
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 14, "Pair your DualSense:");
|
draw_text(kContentX, 14, "Pair your DualSense:");
|
||||||
draw_text(0, 26, "1. Hold Create + PS");
|
draw_text(kContentX, 26, "1. Hold Create + PS");
|
||||||
draw_text(0, 36, "2. Wait for light bar");
|
draw_text(kContentX, 36, "2. Wait for light bar");
|
||||||
draw_text(0, 46, " to flash blue");
|
draw_text(kContentX, 46, " to flash blue");
|
||||||
}
|
}
|
||||||
|
|
||||||
flush_fb();
|
flush_fb();
|
||||||
@@ -528,41 +571,40 @@ __attribute__((noinline)) void render_screen() {
|
|||||||
|
|
||||||
__attribute__((noinline)) void render_screen_rssi() {
|
__attribute__((noinline)) void render_screen_rssi() {
|
||||||
fb_clear();
|
fb_clear();
|
||||||
draw_text(0, 0, "BT Signal");
|
draw_text(kContentX, 0, "BT Signal");
|
||||||
if (bt_is_connected()) {
|
if (bt_is_connected()) {
|
||||||
int8_t rssi = 0;
|
int8_t rssi = 0;
|
||||||
bt_get_signal_strength(&rssi);
|
bt_get_signal_strength(&rssi);
|
||||||
char buf[24];
|
char buf[24];
|
||||||
snprintf(buf, sizeof(buf), "RSSI: %d dBm", (int)rssi);
|
snprintf(buf, sizeof(buf), "RSSI: %d dBm", (int)rssi);
|
||||||
draw_text(0, 12, buf);
|
draw_text(kContentX, 12, buf);
|
||||||
|
|
||||||
// Map RSSI range -90..-40 dBm to 0..100% bar
|
// Map RSSI range -90..-40 dBm to 0..100% bar
|
||||||
int pct = ((int)rssi + 90) * 100 / 50;
|
int pct = ((int)rssi + 90) * 100 / 50;
|
||||||
if (pct < 0) pct = 0;
|
if (pct < 0) pct = 0;
|
||||||
if (pct > 100) pct = 100;
|
if (pct > 100) pct = 100;
|
||||||
snprintf(buf, sizeof(buf), "Quality: %d%%", pct);
|
snprintf(buf, sizeof(buf), "Quality: %d%%", pct);
|
||||||
draw_text(0, 22, buf);
|
draw_text(kContentX, 22, buf);
|
||||||
rect_outline(0, 34, 128, 10);
|
rect_outline(kContentX, 34, 122, 10);
|
||||||
int fill = (pct * 124) / 100;
|
int fill = (pct * 118) / 100;
|
||||||
if (fill > 0) rect_filled(2, 36, fill, 6);
|
if (fill > 0) rect_filled(kContentX + 2, 36, fill, 6);
|
||||||
|
|
||||||
const char *label = "Poor";
|
const char *label = "Poor";
|
||||||
if (rssi > -55) label = "Excellent";
|
if (rssi > -55) label = "Excellent";
|
||||||
else if (rssi > -65) label = "Good";
|
else if (rssi > -65) label = "Good";
|
||||||
else if (rssi > -75) label = "Fair";
|
else if (rssi > -75) label = "Fair";
|
||||||
snprintf(buf, sizeof(buf), "Link: %s", label);
|
snprintf(buf, sizeof(buf), "Link: %s", label);
|
||||||
draw_text(0, 48, buf);
|
draw_text(kContentX, 48, buf);
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 30, "(no controller)");
|
draw_text(kContentX, 30, "(no controller)");
|
||||||
}
|
}
|
||||||
draw_text(0, 56, "K0=next");
|
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void render_screen_diag() {
|
__attribute__((noinline)) void render_screen_diag() {
|
||||||
fb_clear();
|
fb_clear();
|
||||||
|
|
||||||
draw_text(0, 0, "Diagnostics");
|
draw_text(kContentX, 0, "Diagnostics");
|
||||||
|
|
||||||
const uint32_t uptime_s = time_us_32() / 1000000u;
|
const uint32_t uptime_s = time_us_32() / 1000000u;
|
||||||
const uint32_t h = uptime_s / 3600u;
|
const uint32_t h = uptime_s / 3600u;
|
||||||
@@ -570,7 +612,7 @@ __attribute__((noinline)) void render_screen_diag() {
|
|||||||
const uint32_t s = uptime_s % 60u;
|
const uint32_t s = uptime_s % 60u;
|
||||||
char buf[24];
|
char buf[24];
|
||||||
snprintf(buf, sizeof(buf), "Up:%luh %02lum %02lus", (unsigned long)h, (unsigned long)m, (unsigned long)s);
|
snprintf(buf, sizeof(buf), "Up:%luh %02lum %02lus", (unsigned long)h, (unsigned long)m, (unsigned long)s);
|
||||||
draw_text(0, 9, buf);
|
draw_text(kContentX, 9, buf);
|
||||||
|
|
||||||
// Per-second rates for the audio path counters — recompute every render.
|
// Per-second rates for the audio path counters — recompute every render.
|
||||||
static uint32_t prev_us_frames = 0, prev_bt_packets = 0;
|
static uint32_t prev_us_frames = 0, prev_bt_packets = 0;
|
||||||
@@ -591,23 +633,21 @@ __attribute__((noinline)) void render_screen_diag() {
|
|||||||
prev_sample_us = now_us;
|
prev_sample_us = now_us;
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "USB aud %lu/s", (unsigned long)usb_rate);
|
snprintf(buf, sizeof(buf), "USB aud %lu/s", (unsigned long)usb_rate);
|
||||||
draw_text(0, 18, buf);
|
draw_text(kContentX, 18, buf);
|
||||||
snprintf(buf, sizeof(buf), "BT 0x32 %lu/s", (unsigned long)bt_rate);
|
snprintf(buf, sizeof(buf), "BT 0x32 %lu/s", (unsigned long)bt_rate);
|
||||||
draw_text(0, 27, buf);
|
draw_text(kContentX, 27, buf);
|
||||||
snprintf(buf, sizeof(buf), "HCI errs: %lu", (unsigned long)bt_hci_err_count());
|
snprintf(buf, sizeof(buf), "HCI errs: %lu", (unsigned long)bt_hci_err_count());
|
||||||
draw_text(0, 36, buf);
|
draw_text(kContentX, 36, buf);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "BT: %s", bt_is_connected() ? "connected" : "waiting");
|
snprintf(buf, sizeof(buf), "BT: %s", bt_is_connected() ? "connected" : "waiting");
|
||||||
draw_text(0, 45, buf);
|
draw_text(kContentX, 45, buf);
|
||||||
|
|
||||||
draw_text(0, 56, "K0=next K1=back");
|
|
||||||
|
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void render_screen_cpu(bool entered) {
|
__attribute__((noinline)) void render_screen_cpu(bool entered) {
|
||||||
fb_clear();
|
fb_clear();
|
||||||
draw_text(0, 0, "CPU / Clock");
|
draw_text(kContentX, 0, "CPU / Clock");
|
||||||
|
|
||||||
char buf[24];
|
char buf[24];
|
||||||
|
|
||||||
@@ -615,7 +655,7 @@ __attribute__((noinline)) void render_screen_cpu(bool entered) {
|
|||||||
// via set_sys_clock_khz(). This is the *target*.
|
// via set_sys_clock_khz(). This is the *target*.
|
||||||
const uint32_t set_khz = (uint32_t)SYS_CLOCK_KHZ;
|
const uint32_t set_khz = (uint32_t)SYS_CLOCK_KHZ;
|
||||||
snprintf(buf, sizeof(buf), "Set : %lu MHz", (unsigned long)(set_khz / 1000u));
|
snprintf(buf, sizeof(buf), "Set : %lu MHz", (unsigned long)(set_khz / 1000u));
|
||||||
draw_text(0, 12, buf);
|
draw_text(kContentX, 12, buf);
|
||||||
|
|
||||||
// Actually running clk_sys, measured by the on-chip frequency counter
|
// Actually running clk_sys, measured by the on-chip frequency counter
|
||||||
// against the crystal reference (not just what we asked for). The counter
|
// against the crystal reference (not just what we asked for). The counter
|
||||||
@@ -631,7 +671,7 @@ __attribute__((noinline)) void render_screen_cpu(bool entered) {
|
|||||||
snprintf(buf, sizeof(buf), "Real: %lu.%01lu MHz",
|
snprintf(buf, sizeof(buf), "Real: %lu.%01lu MHz",
|
||||||
(unsigned long)(real_khz / 1000u),
|
(unsigned long)(real_khz / 1000u),
|
||||||
(unsigned long)((real_khz % 1000u) / 100u));
|
(unsigned long)((real_khz % 1000u) / 100u));
|
||||||
draw_text(0, 22, buf);
|
draw_text(kContentX, 22, buf);
|
||||||
|
|
||||||
// Core voltage actually programmed into the regulator, read back (not the
|
// Core voltage actually programmed into the regulator, read back (not the
|
||||||
// compile-time constant). Codes 0..15 are linear 0.05 V steps from 0.55 V.
|
// compile-time constant). Codes 0..15 are linear 0.05 V steps from 0.55 V.
|
||||||
@@ -642,7 +682,7 @@ __attribute__((noinline)) void render_screen_cpu(bool entered) {
|
|||||||
} else {
|
} else {
|
||||||
snprintf(buf, sizeof(buf), "Vcore: code %d", vcode);
|
snprintf(buf, sizeof(buf), "Vcore: code %d", vcode);
|
||||||
}
|
}
|
||||||
draw_text(0, 32, buf);
|
draw_text(kContentX, 32, buf);
|
||||||
|
|
||||||
// RP2350 on-die temperature sensor. Smoothed + averaged in cmd.cpp
|
// RP2350 on-die temperature sensor. Smoothed + averaged in cmd.cpp
|
||||||
// (single source of truth shared with the 0xfc web telemetry) so the
|
// (single source of truth shared with the 0xfc web telemetry) so the
|
||||||
@@ -653,52 +693,67 @@ __attribute__((noinline)) void render_screen_cpu(bool entered) {
|
|||||||
const int t10 = (int)(temp_c * 10.0f + (temp_c >= 0 ? 0.5f : -0.5f));
|
const int t10 = (int)(temp_c * 10.0f + (temp_c >= 0 ? 0.5f : -0.5f));
|
||||||
snprintf(buf, sizeof(buf), "Temp : %d.%d C", t10 / 10,
|
snprintf(buf, sizeof(buf), "Temp : %d.%d C", t10 / 10,
|
||||||
(t10 < 0 ? -t10 : t10) % 10);
|
(t10 < 0 ? -t10 : t10) % 10);
|
||||||
draw_text(0, 42, buf);
|
draw_text(kContentX, 42, buf);
|
||||||
|
|
||||||
draw_text(0, 56, "K0=next K1=back");
|
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// △ rising edge on the Trigger Test screen cycles trigger_preset and
|
||||||
|
// re-applies the new effect to the paired controller. KEY1 used to do
|
||||||
|
// this; moving it to the controller frees K0/K1 for navigation only.
|
||||||
|
void triggers_handle_input() {
|
||||||
|
if (!bt_is_connected()) { triggers_last_face = 0; return; }
|
||||||
|
const uint8_t face = interrupt_in_data[7] & 0xF0;
|
||||||
|
const bool tri_now = (face & 0x80) != 0;
|
||||||
|
const bool tri_prev = (triggers_last_face & 0x80) != 0;
|
||||||
|
if (tri_now && !tri_prev) {
|
||||||
|
trigger_preset = (trigger_preset + 1) % kNumTrigPresets;
|
||||||
|
send_trigger_effect(trigger_preset);
|
||||||
|
}
|
||||||
|
triggers_last_face = face;
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void render_screen_triggers() {
|
__attribute__((noinline)) void render_screen_triggers() {
|
||||||
|
triggers_handle_input();
|
||||||
fb_clear();
|
fb_clear();
|
||||||
draw_text(0, 0, "Trigger Test");
|
draw_text(kContentX, 0, "Trigger Test");
|
||||||
|
|
||||||
char buf[24];
|
char buf[24];
|
||||||
snprintf(buf, sizeof(buf), "Mode: %s", kTrigPresetNames[trigger_preset]);
|
snprintf(buf, sizeof(buf), "Mode: %s", kTrigPresetNames[trigger_preset]);
|
||||||
draw_text(0, 12, buf);
|
draw_text(kContentX, 12, buf);
|
||||||
|
|
||||||
if (bt_is_connected()) {
|
if (bt_is_connected()) {
|
||||||
const uint8_t l2 = interrupt_in_data[4];
|
const uint8_t l2 = interrupt_in_data[4];
|
||||||
const uint8_t r2 = interrupt_in_data[5];
|
const uint8_t r2 = interrupt_in_data[5];
|
||||||
snprintf(buf, sizeof(buf), "L2:%3d R2:%3d", l2, r2);
|
snprintf(buf, sizeof(buf), "L2:%3d R2:%3d", l2, r2);
|
||||||
draw_text(0, 24, buf);
|
draw_text(kContentX, 24, buf);
|
||||||
|
|
||||||
rect_outline(0, 35, 60, 9);
|
rect_outline(kContentX, 35, 56, 9);
|
||||||
int lfill = (l2 * 56) / 255;
|
int lfill = (l2 * 52) / 255;
|
||||||
if (lfill > 0) rect_filled(2, 37, lfill, 5);
|
if (lfill > 0) rect_filled(kContentX + 2, 37, lfill, 5);
|
||||||
rect_outline(68, 35, 60, 9);
|
rect_outline(72, 35, 56, 9);
|
||||||
int rfill = (r2 * 56) / 255;
|
int rfill = (r2 * 52) / 255;
|
||||||
if (rfill > 0) rect_filled(70, 37, rfill, 5);
|
if (rfill > 0) rect_filled(74, 37, rfill, 5);
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 24, "(no controller)");
|
draw_text(kContentX, 24, "(no controller)");
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_text(0, 56, "K0=next K1=cycle");
|
draw_text(kContentX, 56, "Tri=cycle");
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void render_screen_gyro() {
|
__attribute__((noinline)) void render_screen_gyro() {
|
||||||
fb_clear();
|
fb_clear();
|
||||||
draw_text(0, 0, "Gyro Tilt");
|
draw_text(kContentX, 0, "Gyro Tilt");
|
||||||
if (bt_is_connected()) {
|
if (bt_is_connected()) {
|
||||||
int16_t ax, ay, az;
|
int16_t ax, ay, az;
|
||||||
memcpy(&ax, &interrupt_in_data[21], 2);
|
memcpy(&ax, &interrupt_in_data[21], 2);
|
||||||
memcpy(&ay, &interrupt_in_data[23], 2);
|
memcpy(&ay, &interrupt_in_data[23], 2);
|
||||||
memcpy(&az, &interrupt_in_data[25], 2);
|
memcpy(&az, &interrupt_in_data[25], 2);
|
||||||
char buf[16];
|
char buf[16];
|
||||||
snprintf(buf, sizeof(buf), "X%+5d", ax); draw_text(0, 10, buf);
|
snprintf(buf, sizeof(buf), "X%+5d", ax); draw_text(kContentX, 10, buf);
|
||||||
snprintf(buf, sizeof(buf), "Y%+5d", ay); draw_text(44, 10, buf);
|
snprintf(buf, sizeof(buf), "Y%+5d", ay); draw_text(50, 10, buf);
|
||||||
snprintf(buf, sizeof(buf), "Z%+5d", az); draw_text(88, 10, buf);
|
snprintf(buf, sizeof(buf), "Z%+5d", az); draw_text(94, 10, buf);
|
||||||
|
|
||||||
const int bx = 44, by = 22, bw = 40, bh = 40;
|
const int bx = 44, by = 22, bw = 40, bh = 40;
|
||||||
rect_outline(bx, by, bw, bh);
|
rect_outline(bx, by, bw, bh);
|
||||||
@@ -714,16 +769,16 @@ __attribute__((noinline)) void render_screen_gyro() {
|
|||||||
if (cy > by + bh - 3) cy = by + bh - 3;
|
if (cy > by + bh - 3) cy = by + bh - 3;
|
||||||
rect_filled(cx - 1, cy - 1, 3, 3);
|
rect_filled(cx - 1, cy - 1, 3, 3);
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 30, "(no controller)");
|
draw_text(kContentX, 30, "(no controller)");
|
||||||
}
|
}
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void render_screen_touchpad() {
|
__attribute__((noinline)) void render_screen_touchpad() {
|
||||||
fb_clear();
|
fb_clear();
|
||||||
draw_text(0, 0, "Touchpad");
|
draw_text(kContentX, 0, "Touchpad");
|
||||||
if (bt_is_connected()) {
|
if (bt_is_connected()) {
|
||||||
rect_outline(4, 12, 120, 30);
|
rect_outline(kContentX + 2, 12, 116, 30);
|
||||||
int active = 0;
|
int active = 0;
|
||||||
for (int finger = 0; finger < 2; finger++) {
|
for (int finger = 0; finger < 2; finger++) {
|
||||||
const int off = 32 + finger * 4;
|
const int off = 32 + finger * 4;
|
||||||
@@ -735,9 +790,9 @@ __attribute__((noinline)) void render_screen_touchpad() {
|
|||||||
if (not_touching) continue;
|
if (not_touching) continue;
|
||||||
const uint16_t fx = (f >> 8) & 0xFFFu;
|
const uint16_t fx = (f >> 8) & 0xFFFu;
|
||||||
const uint16_t fy = (f >> 20) & 0xFFFu;
|
const uint16_t fy = (f >> 20) & 0xFFFu;
|
||||||
int sx = 5 + ((int)fx * 114) / 1919;
|
int sx = (kContentX + 3) + ((int)fx * 110) / 1919;
|
||||||
int sy = 13 + ((int)fy * 26) / 1079;
|
int sy = 13 + ((int)fy * 26) / 1079;
|
||||||
if (sx < 5) sx = 5;
|
if (sx < kContentX + 3) sx = kContentX + 3;
|
||||||
if (sx > 122) sx = 122;
|
if (sx > 122) sx = 122;
|
||||||
if (sy < 13) sy = 13;
|
if (sy < 13) sy = 13;
|
||||||
if (sy > 40) sy = 40;
|
if (sy > 40) sy = 40;
|
||||||
@@ -746,11 +801,10 @@ __attribute__((noinline)) void render_screen_touchpad() {
|
|||||||
}
|
}
|
||||||
char buf[20];
|
char buf[20];
|
||||||
snprintf(buf, sizeof(buf), "Fingers: %d", active);
|
snprintf(buf, sizeof(buf), "Fingers: %d", active);
|
||||||
draw_text(0, 46, buf);
|
draw_text(kContentX, 46, buf);
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 30, "(no controller)");
|
draw_text(kContentX, 30, "(no controller)");
|
||||||
}
|
}
|
||||||
draw_text(0, 56, "K0=next");
|
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -803,9 +857,25 @@ const char* lb_mode_tag(int mode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// R1 rising edge on Lightbar cycles lb_mode. Used to be KEY1; that moved
|
||||||
|
// to back-nav. Triangle on this screen stays as "save current RGB to
|
||||||
|
// favorite slot 0" (the existing favorite-save UX), so R1 is the next
|
||||||
|
// free button that doesn't break a mental model.
|
||||||
|
void lightbar_handle_input() {
|
||||||
|
if (!bt_is_connected()) { lb_last_buttons = 0; return; }
|
||||||
|
const uint8_t btns = interrupt_in_data[8];
|
||||||
|
const bool r1_now = (btns & 0x02) != 0;
|
||||||
|
const bool r1_prev = (lb_last_buttons & 0x02) != 0;
|
||||||
|
if (r1_now && !r1_prev) {
|
||||||
|
lb_mode = (lb_mode + 1) % kNumLbModes;
|
||||||
|
}
|
||||||
|
lb_last_buttons = btns;
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void render_screen_lightbar() {
|
__attribute__((noinline)) void render_screen_lightbar() {
|
||||||
|
lightbar_handle_input();
|
||||||
fb_clear();
|
fb_clear();
|
||||||
draw_text(0, 0, "Lightbar");
|
draw_text(kContentX, 0, "Lightbar");
|
||||||
draw_text(86, 0, lb_mode_tag(lb_mode));
|
draw_text(86, 0, lb_mode_tag(lb_mode));
|
||||||
|
|
||||||
if (bt_is_connected()) {
|
if (bt_is_connected()) {
|
||||||
@@ -853,14 +923,14 @@ __attribute__((noinline)) void render_screen_lightbar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char buf[16];
|
char buf[16];
|
||||||
snprintf(buf, sizeof(buf), "R:%3u", lb_r); draw_text(0, 12, buf);
|
snprintf(buf, sizeof(buf), "R:%3u", lb_r); draw_text(kContentX, 12, buf);
|
||||||
snprintf(buf, sizeof(buf), "G:%3u", lb_g); draw_text(44, 12, buf);
|
snprintf(buf, sizeof(buf), "G:%3u", lb_g); draw_text(48, 12, buf);
|
||||||
snprintf(buf, sizeof(buf), "B:%3u", lb_b); draw_text(88, 12, buf);
|
snprintf(buf, sizeof(buf), "B:%3u", lb_b); draw_text(90, 12, buf);
|
||||||
|
|
||||||
const int by = 22, bh = 8;
|
const int by = 22, bh = 8;
|
||||||
rect_outline(0, by, 40, bh); int rf = (lb_r * 36) / 255; if (rf > 0) rect_filled(2, by + 2, rf, bh - 4);
|
rect_outline(kContentX, by, 38, bh); int rf = (lb_r * 34) / 255; if (rf > 0) rect_filled(kContentX + 2, by + 2, rf, bh - 4);
|
||||||
rect_outline(44, by, 40, bh); int gf = (lb_g * 36) / 255; if (gf > 0) rect_filled(46, by + 2, gf, bh - 4);
|
rect_outline(48, by, 38, bh); int gf = (lb_g * 34) / 255; if (gf > 0) rect_filled(50, by + 2, gf, bh - 4);
|
||||||
rect_outline(88, by, 40, bh); int bf = (lb_b * 36) / 255; if (bf > 0) rect_filled(90, by + 2, bf, bh - 4);
|
rect_outline(90, by, 38, bh); int bf = (lb_b * 34) / 255; if (bf > 0) rect_filled(92, by + 2, bf, bh - 4);
|
||||||
|
|
||||||
// Face button rising-edge -> save current color to slot 0..3
|
// Face button rising-edge -> save current color to slot 0..3
|
||||||
const uint8_t face = interrupt_in_data[7] & 0xF0;
|
const uint8_t face = interrupt_in_data[7] & 0xF0;
|
||||||
@@ -877,47 +947,46 @@ __attribute__((noinline)) void render_screen_lightbar() {
|
|||||||
lb_fav_b[save_slot] = lb_b;
|
lb_fav_b[save_slot] = lb_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_text(0, 38, "Sv:T=0 C=1 X=2 S=3");
|
draw_text(kContentX, 38, "Sv:T=0 C=1 X=2 S=3");
|
||||||
const char* hint =
|
const char* hint =
|
||||||
(lb_mode == 0) ? "Tilt = R/G/B" :
|
(lb_mode == 0) ? "Tilt = R/G/B" :
|
||||||
(lb_mode == 5) ? "Breathing FAV0" :
|
(lb_mode == 5) ? "Breathing FAV0" :
|
||||||
(lb_mode == 6) ? "Rainbow sweep" :
|
(lb_mode == 6) ? "Rainbow sweep" :
|
||||||
(lb_mode == 7) ? "Fade thru FAVs" :
|
(lb_mode == 7) ? "Fade thru FAVs" :
|
||||||
"Locked to fav";
|
"Locked to fav";
|
||||||
draw_text(0, 48, hint);
|
draw_text(kContentX, 48, hint);
|
||||||
|
|
||||||
send_lightbar_color(lb_r, lb_g, lb_b);
|
send_lightbar_color(lb_r, lb_g, lb_b);
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 30, "(no controller)");
|
draw_text(kContentX, 30, "(no controller)");
|
||||||
}
|
}
|
||||||
draw_text(0, 56, "K0=next K1=cycle");
|
draw_text(kContentX, 56, "R1=mode");
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void render_screen_vu() {
|
__attribute__((noinline)) void render_screen_vu() {
|
||||||
fb_clear();
|
fb_clear();
|
||||||
draw_text(0, 0, "Audio Meters");
|
draw_text(kContentX, 0, "Audio Meters");
|
||||||
if (bt_is_connected()) {
|
if (bt_is_connected()) {
|
||||||
const uint8_t spk = audio_peak_speaker();
|
const uint8_t spk = audio_peak_speaker();
|
||||||
const uint8_t hap = audio_peak_haptic();
|
const uint8_t hap = audio_peak_haptic();
|
||||||
char buf[16];
|
char buf[16];
|
||||||
snprintf(buf, sizeof(buf), "SPK %3u", spk);
|
snprintf(buf, sizeof(buf), "SPK %3u", spk);
|
||||||
draw_text(0, 14, buf);
|
draw_text(kContentX, 14, buf);
|
||||||
rect_outline(48, 14, 80, 8);
|
rect_outline(48, 14, 80, 8);
|
||||||
int sfill = (spk * 76) / 255;
|
int sfill = (spk * 76) / 255;
|
||||||
if (sfill > 0) rect_filled(50, 16, sfill, 4);
|
if (sfill > 0) rect_filled(50, 16, sfill, 4);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "HAP %3u", hap);
|
snprintf(buf, sizeof(buf), "HAP %3u", hap);
|
||||||
draw_text(0, 28, buf);
|
draw_text(kContentX, 28, buf);
|
||||||
rect_outline(48, 28, 80, 8);
|
rect_outline(48, 28, 80, 8);
|
||||||
int hfill = (hap * 76) / 255;
|
int hfill = (hap * 76) / 255;
|
||||||
if (hfill > 0) rect_filled(50, 30, hfill, 4);
|
if (hfill > 0) rect_filled(50, 30, hfill, 4);
|
||||||
|
|
||||||
draw_text(0, 42, "Live USB audio peaks");
|
draw_text(kContentX, 42, "Live USB audio peaks");
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 30, "(no controller)");
|
draw_text(kContentX, 30, "(no controller)");
|
||||||
}
|
}
|
||||||
draw_text(0, 56, "K0=next");
|
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1088,7 +1157,7 @@ __attribute__((noinline)) void render_screen_settings() {
|
|||||||
fb_clear();
|
fb_clear();
|
||||||
char buf[24];
|
char buf[24];
|
||||||
snprintf(buf, sizeof(buf), "Settings %s", settings_dirty ? "(*)" : " ");
|
snprintf(buf, sizeof(buf), "Settings %s", settings_dirty ? "(*)" : " ");
|
||||||
draw_text(0, 0, buf);
|
draw_text(kContentX, 0, buf);
|
||||||
if (settings_save_status[0]) {
|
if (settings_save_status[0]) {
|
||||||
draw_text(86, 0, settings_save_status);
|
draw_text(86, 0, settings_save_status);
|
||||||
}
|
}
|
||||||
@@ -1099,15 +1168,15 @@ __attribute__((noinline)) void render_screen_settings() {
|
|||||||
char line[28];
|
char line[28];
|
||||||
for (int i = 0; i < kVisible && top + i < kNumSettingsItems; i++) {
|
for (int i = 0; i < kVisible && top + i < kNumSettingsItems; i++) {
|
||||||
format_settings_item(top + i, line, sizeof(line));
|
format_settings_item(top + i, line, sizeof(line));
|
||||||
draw_text(0, 9 + i * 9, line);
|
draw_text(kContentX, 9 + i * 9, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings_sel == kSettingsResetIdx) {
|
if (settings_sel == kSettingsResetIdx) {
|
||||||
draw_text(0, 56, "Hold Tri 2s = RESET");
|
draw_text(kContentX, 56, "Hold Tri 2s = RESET");
|
||||||
} else if (settings_sel == kSettingsWipeSlotsIdx) {
|
} else if (settings_sel == kSettingsWipeSlotsIdx) {
|
||||||
draw_text(0, 56, "Hold Tri 2s = WIPE");
|
draw_text(kContentX, 56, "Hold Tri 2s = WIPE");
|
||||||
} else {
|
} else {
|
||||||
draw_text(0, 56, "DP nav/adj Tri=save");
|
draw_text(kContentX, 56, "DP nav/adj Tri=save");
|
||||||
}
|
}
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
@@ -1183,7 +1252,7 @@ __attribute__((noinline)) void render_screen_slots() {
|
|||||||
const int active = bt_get_slot();
|
const int active = bt_get_slot();
|
||||||
const bool conn = bt_is_connected();
|
const bool conn = bt_is_connected();
|
||||||
snprintf(hdr, sizeof(hdr), "Slots [s%d %s]", active, conn ? "ON" : "--");
|
snprintf(hdr, sizeof(hdr), "Slots [s%d %s]", active, conn ? "ON" : "--");
|
||||||
draw_text(0, 0, hdr);
|
draw_text(kContentX, 0, hdr);
|
||||||
|
|
||||||
if (slots_status[0] && (uint32_t)time_us_32() < slots_status_until_us) {
|
if (slots_status[0] && (uint32_t)time_us_32() < slots_status_until_us) {
|
||||||
draw_text(80, 0, slots_status);
|
draw_text(80, 0, slots_status);
|
||||||
@@ -1201,10 +1270,10 @@ __attribute__((noinline)) void render_screen_slots() {
|
|||||||
} else {
|
} else {
|
||||||
snprintf(line, sizeof(line), "%s%d%s (empty)", cursor_mark, i, active_mark);
|
snprintf(line, sizeof(line), "%s%d%s (empty)", cursor_mark, i, active_mark);
|
||||||
}
|
}
|
||||||
draw_text(0, 9 + i * 9, line);
|
draw_text(kContentX, 9 + i * 9, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_text(0, 56, "Tri=switch Sq hold=wipe");
|
draw_text(kContentX, 56, "Tri=switch Sq hold=wipe");
|
||||||
flush_fb();
|
flush_fb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user