mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-03 13:29:01 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2ad0a1b1a | |||
| 96cdb2e9e0 | |||
| 002ef720ee | |||
| 2214533894 | |||
| 06b7a0419e |
@@ -1 +1 @@
|
||||
v1.7.2
|
||||
v1.7.3
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.7.3
|
||||
v1.7.4
|
||||
|
||||
@@ -5607,6 +5607,44 @@ static constexpr Bitmap bitmap_icon_speaker_and_headphones_mute{
|
||||
{16, 16},
|
||||
bitmap_icon_speaker_and_headphones_mute_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_shift_data[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
0x80,
|
||||
0x00,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xE0,
|
||||
0x03,
|
||||
0xF0,
|
||||
0x07,
|
||||
0xF8,
|
||||
0x0F,
|
||||
0xFC,
|
||||
0x1F,
|
||||
0xE0,
|
||||
0x03,
|
||||
0xE0,
|
||||
0x03,
|
||||
0xE0,
|
||||
0x03,
|
||||
0x20,
|
||||
0x02,
|
||||
0xE0,
|
||||
0x03,
|
||||
0x20,
|
||||
0x02,
|
||||
0xE0,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_shift{
|
||||
{16, 16},
|
||||
bitmap_icon_shift_data};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif /*__BITMAP_HPP__*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2023 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -24,9 +25,8 @@
|
||||
#include "utility.hpp"
|
||||
|
||||
uint8_t Debounce::state() {
|
||||
bool v = !pulse_upon_release_ && (state_ || simulated_pulse_);
|
||||
if (simulated_pulse_)
|
||||
simulated_pulse_ = false;
|
||||
bool v = state_to_report_;
|
||||
simulated_pulse_ = false;
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -52,84 +52,100 @@ bool Debounce::long_press_occurred() {
|
||||
bool Debounce::feed(const uint8_t bit) {
|
||||
history_ = (history_ << 1) | (bit & 1);
|
||||
|
||||
// "Repeat" handling - simulated button release
|
||||
if (repeat_ctr_) {
|
||||
// Make sure the button is still being held continuously
|
||||
if ((history_ == 0xFF) && !long_press_enabled_) {
|
||||
// Simulate button press every REPEAT_SUBSEQUENT_DELAY ticks
|
||||
if (--repeat_ctr_ == 0) {
|
||||
state_ = !state_;
|
||||
repeat_ctr_ = REPEAT_SUBSEQUENT_DELAY / 2;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// It's a real button release; stop simulating
|
||||
repeat_ctr_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (state_ == 0) {
|
||||
// Previous button state was 0 (released);
|
||||
// Has button been held for DEBOUNCE_COUNT ticks?
|
||||
if ((history_ & DEBOUNCE_MASK) == DEBOUNCE_MASK) {
|
||||
// Has button been held for DEBOUNCE_COUNT ticks?
|
||||
if ((history_ & DEBOUNCE_MASK) == DEBOUNCE_MASK) {
|
||||
//
|
||||
// Button is currently pressed;
|
||||
// Was previous button state 0 (released)?
|
||||
//
|
||||
if (state_ == 0) {
|
||||
//
|
||||
// Button has been pressed (after filtering glitches), transition 0->1
|
||||
//
|
||||
state_ = 1;
|
||||
held_time_ = 0;
|
||||
|
||||
// If long_press_enabled_, state() function masks the button press until it's released
|
||||
// or until LONG_PRESS_DELAY is reached
|
||||
if (long_press_enabled_) {
|
||||
pulse_upon_release_ = true;
|
||||
state_to_report_ = 0;
|
||||
return false;
|
||||
}
|
||||
state_to_report_ = 1;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Previous button state was 1 (pressed);
|
||||
// Has button been released for DEBOUNCE_COUNT ticks?
|
||||
if ((history_ & DEBOUNCE_MASK) == 0) {
|
||||
// Button has been released when long_press_enabled_ and before LONG_PRESS_DELAY was reached;
|
||||
|
||||
// "Repeat" handling - simulated button release
|
||||
if (repeat_ctr_ && !long_press_enabled_) {
|
||||
// Simulate button press every REPEAT_SUBSEQUENT_DELAY ticks
|
||||
// (by toggling reported state every 1/2 of the delay time)
|
||||
if (--repeat_ctr_ == 0) {
|
||||
state_to_report_ = !state_to_report_;
|
||||
repeat_ctr_ = REPEAT_SUBSEQUENT_DELAY / 2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of how long button has been held
|
||||
held_time_++;
|
||||
if (pulse_upon_release_) {
|
||||
// Button is being held down and long_press support is enabled for this key:
|
||||
// if LONG_PRESS_DELAY is reached then finally report that switch is pressed and set flag
|
||||
// indicating it was a LONG press
|
||||
// (note that repeat_support and long_press support are mutually exclusive)
|
||||
if (held_time_ >= LONG_PRESS_DELAY) {
|
||||
pulse_upon_release_ = false;
|
||||
long_press_occurred_ = true;
|
||||
state_to_report_ = 1;
|
||||
return true;
|
||||
}
|
||||
} else if (repeat_enabled_ && !long_press_enabled_) {
|
||||
// Repeat support -- 4 directional buttons only (unless long_press is enabled)
|
||||
if (held_time_ >= REPEAT_INITIAL_DELAY) {
|
||||
// Delay reached; trigger repeat code on NEXT tick
|
||||
repeat_ctr_ = 1;
|
||||
held_time_ = 0;
|
||||
}
|
||||
}
|
||||
} else if ((history_ & DEBOUNCE_MASK) == 0) { // Has button been released for at least DEBOUNCE_COUNT ticks?
|
||||
//
|
||||
// Button is released;
|
||||
// Was previous button state 1 (pressed)?
|
||||
//
|
||||
if (state_ == 1) {
|
||||
//
|
||||
// Button has been released (after filtering glitches), transition 1->0
|
||||
//
|
||||
state_ = 0;
|
||||
long_press_occurred_ = false;
|
||||
|
||||
// If button released when long_press_enabled_ and before LONG_PRESS_DELAY was reached;
|
||||
// allow state() function to finally return a single press indication (simulated pulse).
|
||||
// Note: In long press mode, apps won't see button press until the button is released.
|
||||
if (pulse_upon_release_) {
|
||||
// force state() function (called by EventDispatcher) to return simulated press for one cycle
|
||||
simulated_pulse_ = true;
|
||||
pulse_upon_release_ = false;
|
||||
} else {
|
||||
state_ = 0;
|
||||
simulated_pulse_ = true;
|
||||
state_to_report_ = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reset long_press_occurred_ flag after button is released
|
||||
long_press_occurred_ = false;
|
||||
state_to_report_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Has button been held continuously?
|
||||
if (history_ == 0xFF) {
|
||||
held_time_++;
|
||||
if (pulse_upon_release_) {
|
||||
// Button is being held down and long_press support is enabled for this key:
|
||||
// if LONG_PRESS_DELAY is reached then finally report that switch is pressed and set flag
|
||||
// indicating it was a LONG press
|
||||
// (note that repeat_support and long_press support are mutually exclusive)
|
||||
if (held_time_ >= LONG_PRESS_DELAY) {
|
||||
long_press_occurred_ = true;
|
||||
simulated_pulse_ = true;
|
||||
pulse_upon_release_ = false;
|
||||
held_time_ = 0;
|
||||
return true;
|
||||
}
|
||||
} else if (repeat_enabled_ && !long_press_enabled_) {
|
||||
// Repeat support -- 4 directional buttons only (unless long_press is enabled)
|
||||
if (held_time_ == REPEAT_INITIAL_DELAY) {
|
||||
// Delay reached; trigger repeat code on NEXT tick
|
||||
repeat_ctr_ = 1;
|
||||
held_time_ = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Button not continuously pressed; reset counter
|
||||
held_time_ = 0;
|
||||
// Reset reported state after application/event has cleared simulated_pulse_
|
||||
if (state_to_report_ == 1 && !simulated_pulse_) {
|
||||
state_to_report_ = 0;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// Button is in transition between states;
|
||||
// Reset counters until button inputs are stable.
|
||||
//
|
||||
held_time_ = 0;
|
||||
repeat_ctr_ = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -43,15 +43,25 @@ class Debounce {
|
||||
bool long_press_occurred();
|
||||
|
||||
private:
|
||||
uint8_t history_{0};
|
||||
uint8_t state_{0};
|
||||
bool repeat_enabled_{false};
|
||||
uint16_t repeat_ctr_{0};
|
||||
uint16_t held_time_{0};
|
||||
bool pulse_upon_release_{false};
|
||||
bool simulated_pulse_{false};
|
||||
bool long_press_enabled_{false};
|
||||
bool long_press_occurred_{false};
|
||||
uint8_t history_{0}; // shift register of last 8 reads from button hardware state bit
|
||||
|
||||
uint8_t state_{0}; // actual button hardware state (after debounce logic), 1=pressed
|
||||
|
||||
uint8_t state_to_report_{0}; // pseudo button state reported by state() function (may be masked off or simulated presses)
|
||||
|
||||
bool repeat_enabled_{false}; // TRUE if this button is enabled to auto-repeat when held down (ignored if long_press_enabled)
|
||||
|
||||
uint16_t repeat_ctr_{0}; // used for timing auto-repeat simulated button presses when button is held down and repeat_enabled
|
||||
|
||||
uint16_t held_time_{0}; // number of ticks that the button has been held down (compared against REPEAT and LONG_PRESS delays)
|
||||
|
||||
bool pulse_upon_release_{false}; // TRUE when button is being held down when long_press_enabled and LONG_PRESS_DELAY hasn't been reached yet
|
||||
|
||||
bool simulated_pulse_{false}; // TRUE if a simulated button press is active following a short button press (only when long_press_enabled)
|
||||
|
||||
bool long_press_enabled_{false}; // TRUE when button is in long-press mode (takes precedence over the repeat_enabled flag)
|
||||
|
||||
bool long_press_occurred_{false}; // TRUE when button is being held down and LONG_PRESS_DELAY has been reached (only when long_press_enabled)
|
||||
};
|
||||
|
||||
#endif /*__DEBOUNCE_H__*/
|
||||
|
||||
@@ -22,11 +22,10 @@
|
||||
|
||||
#include "ui_alphanum.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -38,6 +37,7 @@ AlphanumView::AlphanumView(
|
||||
size_t n;
|
||||
|
||||
add_children({
|
||||
&button_shift,
|
||||
&labels,
|
||||
&field_raw,
|
||||
&text_raw_to_char,
|
||||
@@ -49,6 +49,19 @@ AlphanumView::AlphanumView(
|
||||
this->on_button(button);
|
||||
};
|
||||
|
||||
button_shift.set_vertical_center(true);
|
||||
button_shift.on_select = [this]() {
|
||||
incr(shift_mode);
|
||||
|
||||
// Disallow one-time shift in digits/symbols mode
|
||||
if ((mode == 1) && (shift_mode == ShiftMode::Shift))
|
||||
shift_mode = ShiftMode::ShiftLock;
|
||||
else if (shift_mode > ShiftMode::ShiftLock)
|
||||
shift_mode = ShiftMode::None;
|
||||
|
||||
refresh_keys();
|
||||
};
|
||||
|
||||
n = 0;
|
||||
for (auto& button : buttons) {
|
||||
button.id = n;
|
||||
@@ -56,9 +69,9 @@ AlphanumView::AlphanumView(
|
||||
focused_button = button.id;
|
||||
};
|
||||
button.on_select = button_fn;
|
||||
button.set_parent_rect({static_cast<Coord>((n % 5) * (240 / 5)),
|
||||
button.set_parent_rect({static_cast<Coord>((n % 5) * (screen_width / 5)),
|
||||
static_cast<Coord>((n / 5) * 38 + 24),
|
||||
240 / 5, 38});
|
||||
screen_width / 5, 38});
|
||||
add_child(&button);
|
||||
n++;
|
||||
}
|
||||
@@ -78,38 +91,59 @@ AlphanumView::AlphanumView(
|
||||
char_add(field_raw.value());
|
||||
};
|
||||
|
||||
// make text_raw_to_char widget display the char value from field_raw
|
||||
field_raw.on_change = [this](auto) {
|
||||
text_raw_to_char.set(std::string{static_cast<char>(field_raw.value())});
|
||||
};
|
||||
}
|
||||
|
||||
void AlphanumView::set_mode(const uint32_t new_mode) {
|
||||
size_t n = 0;
|
||||
|
||||
if (new_mode < 3)
|
||||
void AlphanumView::set_mode(uint32_t new_mode, ShiftMode new_shift_mode) {
|
||||
if (new_mode < std::size(key_sets))
|
||||
mode = new_mode;
|
||||
else
|
||||
mode = 0;
|
||||
|
||||
const char* key_list = key_sets[mode].second;
|
||||
shift_mode = new_shift_mode;
|
||||
refresh_keys();
|
||||
|
||||
if (mode + 1 < std::size(key_sets))
|
||||
button_mode.set_text(key_sets[mode + 1].name);
|
||||
else
|
||||
button_mode.set_text(key_sets[0].name);
|
||||
}
|
||||
|
||||
void AlphanumView::refresh_keys() {
|
||||
auto key_list = shift_mode == ShiftMode::None
|
||||
? key_sets[mode].normal
|
||||
: key_sets[mode].shifted;
|
||||
|
||||
size_t n = 0;
|
||||
for (auto& button : buttons) {
|
||||
const std::string label{
|
||||
key_list[n]};
|
||||
button.set_text(label);
|
||||
button.set_text(std::string{key_list[n]});
|
||||
n++;
|
||||
}
|
||||
|
||||
if (mode < 2)
|
||||
button_mode.set_text(key_sets[mode + 1].first);
|
||||
else
|
||||
button_mode.set_text(key_sets[0].first);
|
||||
switch (shift_mode) {
|
||||
case ShiftMode::None:
|
||||
button_shift.set_color(Color::dark_grey());
|
||||
break;
|
||||
case ShiftMode::Shift:
|
||||
button_shift.set_color(Color::black());
|
||||
break;
|
||||
case ShiftMode::ShiftLock:
|
||||
button_shift.set_color(Color::dark_blue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AlphanumView::on_button(Button& button) {
|
||||
const auto c = button.text()[0];
|
||||
char_add(c);
|
||||
|
||||
// TODO: Consolidate shift handling.
|
||||
if (shift_mode == ShiftMode::Shift) {
|
||||
shift_mode = ShiftMode::None;
|
||||
refresh_keys();
|
||||
}
|
||||
}
|
||||
|
||||
bool AlphanumView::on_encoder(const EncoderEvent delta) {
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
#include "ui_textentry.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
|
||||
// TODO: Building this as a custom widget instead of using
|
||||
// all the Button controls would save a considerable amount of RAM.
|
||||
// The Buttons each have a backing string but these only need one char.
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AlphanumView : public TextEntryView {
|
||||
@@ -43,22 +47,42 @@ class AlphanumView : public TextEntryView {
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
|
||||
private:
|
||||
const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ, ._";
|
||||
const char* const keys_lower = "abcdefghijklmnopqrstuvwxyz, ._";
|
||||
const char* const keys_digit = "0123456789!\"#'()*+-/:;=<>@[\\]?";
|
||||
enum class ShiftMode : uint8_t {
|
||||
None,
|
||||
Shift,
|
||||
ShiftLock,
|
||||
};
|
||||
|
||||
const std::pair<std::string, const char*> key_sets[3] = {
|
||||
{"ABC", keys_upper},
|
||||
{"abc", keys_lower},
|
||||
{"123", keys_digit}};
|
||||
const char* const keys_lower = "abcdefghijklmnopqrstuvwxyz, .";
|
||||
const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ, .";
|
||||
const char* const keys_digit = "1234567890()'`\"+-*/=<>_\\!?, .";
|
||||
const char* const keys_symbl = "!@#$%^&*()[]'`\"{}|:;<>-_~?, .";
|
||||
|
||||
struct key_set_t {
|
||||
const char* name;
|
||||
const char* normal;
|
||||
const char* shifted;
|
||||
};
|
||||
|
||||
const key_set_t key_sets[2] = {
|
||||
{"abc", keys_lower, keys_upper},
|
||||
{"123", keys_digit, keys_symbl}};
|
||||
|
||||
int16_t focused_button = 0;
|
||||
uint32_t mode = 0; // Uppercase
|
||||
uint32_t mode = 0; // Letters.
|
||||
ShiftMode shift_mode = ShiftMode::None;
|
||||
|
||||
void set_mode(const uint32_t new_mode);
|
||||
void set_mode(uint32_t new_mode, ShiftMode new_shift_mode = ShiftMode::None);
|
||||
void refresh_keys();
|
||||
void on_button(Button& button);
|
||||
|
||||
std::array<Button, 30> buttons{};
|
||||
std::array<Button, 29> buttons{};
|
||||
|
||||
NewButton button_shift{
|
||||
{192, 214, screen_width / 5, 38},
|
||||
{},
|
||||
&bitmap_icon_shift,
|
||||
Color::dark_grey()};
|
||||
|
||||
Labels labels{
|
||||
{{1 * 8, 33 * 8}, "Raw:", Color::light_grey()},
|
||||
@@ -76,11 +100,11 @@ class AlphanumView : public TextEntryView {
|
||||
"0"};
|
||||
|
||||
Button button_delete{
|
||||
{9 * 8, 33 * 8, 6 * 8, 32},
|
||||
{9 * 8, 32 * 8 - 3, 7 * 8, 3 * 16 + 3},
|
||||
"<DEL"};
|
||||
|
||||
Button button_mode{
|
||||
{16 * 8, 33 * 8, 5 * 8, 32},
|
||||
{16 * 8, 32 * 8 - 3, 6 * 8, 3 * 16 + 3},
|
||||
""};
|
||||
};
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "ui_textentry.hpp"
|
||||
//#include "portapack_persistent_memory.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
//#include "ui_handwrite.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class TextEntryView : public View {
|
||||
|
||||
TextEdit text_input;
|
||||
Button button_ok{
|
||||
{22 * 8, 33 * 8, 7 * 8, 32},
|
||||
{22 * 8, 32 * 8 - 3, 8 * 8, 3 * 16 + 3},
|
||||
"OK"};
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ void ReplayProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
if (!configured || !stream) return;
|
||||
|
||||
buffer_c16_t iq_buffer{iq.data(), iq.size(), baseband_fs / 8};
|
||||
|
||||
// File data is in C16 format, we need C8
|
||||
// File samplerate is 500kHz, we're at 4MHz
|
||||
// iq_buffer can only be 512 C16 samples (RAM limitation)
|
||||
|
||||
@@ -45,10 +45,6 @@ class ReplayProcessor : public BasebandProcessor {
|
||||
static constexpr auto spectrum_rate_hz = 50.0f;
|
||||
|
||||
std::array<complex16_t, 256> iq{};
|
||||
const buffer_c16_t iq_buffer{
|
||||
iq.data(),
|
||||
iq.size(),
|
||||
baseband_fs / 8};
|
||||
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
|
||||
@@ -1188,7 +1188,7 @@ void NewButton::paint(Painter& painter) {
|
||||
painter.draw_bitmap(
|
||||
bmp_pos,
|
||||
*bitmap_,
|
||||
color_, // Color::green(), //fg,
|
||||
color_,
|
||||
bg);
|
||||
}
|
||||
|
||||
@@ -1667,8 +1667,6 @@ void TextEdit::char_delete() {
|
||||
}
|
||||
|
||||
void TextEdit::paint(Painter& painter) {
|
||||
constexpr int char_width = 8;
|
||||
|
||||
auto rect = screen_rect();
|
||||
auto text_style = has_focus() ? style().invert() : style();
|
||||
auto offset = 0;
|
||||
@@ -1677,15 +1675,14 @@ void TextEdit::paint(Painter& painter) {
|
||||
if (cursor_pos_ >= char_count_)
|
||||
offset = cursor_pos_ - char_count_ + 1;
|
||||
|
||||
// Clear the control.
|
||||
painter.fill_rectangle(rect, text_style.background);
|
||||
|
||||
// Draw the text starting at the offset.
|
||||
for (uint32_t i = 0; i < char_count_ && i + offset < text_.length(); i++) {
|
||||
for (uint32_t i = 0; i < char_count_; i++) {
|
||||
// Using draw_char to blank the rest of the line with spaces produces less flicker.
|
||||
auto c = (i + offset < text_.length()) ? text_[i + offset] : ' ';
|
||||
|
||||
painter.draw_char(
|
||||
{rect.location().x() + (static_cast<int>(i) * char_width), rect.location().y()},
|
||||
text_style,
|
||||
text_[i + offset]);
|
||||
text_style, c);
|
||||
}
|
||||
|
||||
// Determine cursor position on screen (either the cursor position or the last char).
|
||||
@@ -1698,7 +1695,7 @@ void TextEdit::paint(Painter& painter) {
|
||||
painter.draw_char(cursor_point, cursor_style, text_[cursor_pos_]);
|
||||
|
||||
// Draw the cursor.
|
||||
Rect cursor_box{cursor_point, {char_width, 16}};
|
||||
Rect cursor_box{cursor_point, {char_width, char_height}};
|
||||
painter.draw_rectangle(cursor_box, cursor_style.background);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 267 B |
Reference in New Issue
Block a user