Compare commits

...

16 Commits

Author SHA1 Message Date
E.T ea8563cb19 Fix workflow to pick up the OCI named ppfw (#1787) 2024-01-18 21:08:02 +01:00
Mark Thompson b3c1c83677 Remove time from PPFW filename and modify Flash app to support .tar (#1783)
* Update timestamp in PPFW filename

* Only include today's DATE in filename

* Remove date from PPFW filename

* Allow any .tar file name in FIRMWARE folder

* Add files via upload

* Removed unnecessary lines per ufoka
2024-01-18 13:59:44 -06:00
Totoo fcbc3b4d75 pmemreset and settingsreset commands (#1786) 2024-01-18 16:12:13 +01:00
Mark Thompson 39e1a0ffe8 Cmake - generate new ppfw when firmware changes (#1782) 2024-01-17 07:01:48 +01:00
Mark Thompson 99bbd8805c Added Settings app to disable Config Mode (#1781) 2024-01-17 06:59:35 +01:00
Mark Thompson e7af85ffba Add new pmem fields to Debug Dump (#1780) 2024-01-16 22:49:56 +01:00
Mark Thompson 75f3277102 Geomap zoom-out fixes (#1777) 2024-01-16 18:47:31 +01:00
Totoo 216f8b64bf Fix build after merge (#1776) 2024-01-16 10:05:59 +01:00
Mark Thompson bc301c5fdb Geomap enhancements (#1767)
* Increase Geomap zoom-in ability
* Use floating point for current position
* Show grid squares when zoomed in too much for map
* Zoom in fast after exceeding map resolution & clean up redundant code
* Revert order of functions to make it easier to review
* Changed grid color for better contrast with markers
* Optimizations
* Set x_pos/x_pos to center pixel versus upper left corner
* Show more distant planes when zoomed out
* Correct pixel offset when zooming in
* Fix oops in x_pos/y_pos centering change
* Wrapping support for lat/lon fields
* Wrapping support (for Geomap lat/lon fields)
* Handle wrapping for negative lat/lon
2024-01-16 09:30:31 +01:00
gullradriel ae9d6de093 added sysinfo and radioinfo (#1769)
* added sysinfo and radioinfo

* using fillObuffer instead of chprintf

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-01-15 15:54:27 +13:00
Bernd Herzog 7a4c3184a1 reworked usb serial communication (#1766) 2024-01-13 18:05:29 +01:00
Mark Thompson 58307aee9c Make use of "ensure_directory" function (#1765) 2024-01-13 07:57:32 +01:00
Bernd Herzog 409242507c Usb serial fix queue full crash (#1763)
* fixed usb serial queue crashing

* fixed usb input buffer handling

* fixed black screen issue
2024-01-12 21:49:10 +01:00
E.T 0a8194fa10 Really go home before starting app from serial (#1762) 2024-01-12 17:33:41 +01:00
Bernd Herzog eaac40df3a fixed usb serial queue crashing (#1761) 2024-01-12 16:04:32 +01:00
Mark Thompson 58b488b840 Added new "App Setting" app (#1759)
* New "App Settings" app for quicker settings access
2024-01-12 13:41:13 +01:00
35 changed files with 771 additions and 285 deletions
+1 -1
View File
@@ -100,7 +100,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: build/firmware/portapack-mayhem.ppfw.tar
asset_path: build/firmware/portapack-mayhem_OCI.ppfw.tar
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar
asset_content_type: application/x-tar
- name: Upload Firmware Asset
+1 -1
View File
@@ -94,7 +94,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: build/firmware/portapack-mayhem.ppfw.tar
asset_path: build/firmware/portapack-mayhem_OCI.ppfw.tar
asset_name: mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar
asset_content_type: application/x-tar
- name: Upload Firmware Asset
-11
View File
@@ -46,7 +46,6 @@ if ("${VERSION}" STREQUAL "")
set(VERSION_NOHASH "dev")
else()
set(VERSION_NOHASH "${VERSION}")
set(PPFW_FILENAME "portapack-mayhem.ppfw.tar")
endif()
execute_process(
@@ -55,16 +54,6 @@ execute_process(
)
set(VERSION_MD5 "0x${VERSION_MD5}")
if (NOT DEFINED PPFW_FILENAME)
# Funny business here: trying to generate unique names to prevent renaming of shared test-drive builds by the browser on download to .ppfw-42.tar as it wont be recognized by the flasher
execute_process(
COMMAND date "+%y%m%d-%H%M"
OUTPUT_VARIABLE PPFW_FILENAME
)
string(STRIP ${PPFW_FILENAME} PPFW_FILENAME)
set(PPFW_FILENAME "portapack-mayhem_${PPFW_FILENAME}_OCI.ppfw.tar")
endif()
message("Building version: ${VERSION} MD5: ${VERSION_MD5}")
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
+2 -1
View File
@@ -35,6 +35,7 @@ set(LZ4 lz4)
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
set(PPFW_FILENAME "portapack-mayhem_OCI.ppfw.tar")
# In our current build container cmake need a little help to get the version :)
if(NOT DEFINED ${CMAKE_CXX_COMPILER_VERSION})
@@ -100,7 +101,7 @@ add_custom_command(
COMMAND mkdir -p firmware_tar/APPS
COMMAND cp application/*.ppma firmware_tar/APPS
COMMAND cd firmware_tar && tar -cvaf ../${PPFW_FILENAME} *
DEPENDS firmware
DEPENDS firmware ${FIRMWARE_FILENAME}
# Dont use VERBATIM here as it prevents usage of globbing (*)
# There shouldnt be any funny business in the filenames above :)
)
+2 -1
View File
@@ -116,7 +116,7 @@ set(CSRC
usb_serial_cdc.c
usb_serial_descriptor.c
usb_serial_endpoints.c
usb_serial_io.c
usb_serial_device_to_host.c
${HACKRF_PATH}/firmware/common/usb.c
${HACKRF_PATH}/firmware/common/usb_queue.c
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
@@ -207,6 +207,7 @@ set(CPPSRC
usb_serial_event.cpp
usb_serial_thread.cpp
usb_serial.cpp
usb_serial_host_to_device.cpp
qrcodegen.cpp
radio.cpp
receiver_model.cpp
+2 -2
View File
@@ -40,7 +40,7 @@ using namespace portapack;
namespace {
fs::path get_settings_path(const std::string& app_name) {
return fs::path{u"/SETTINGS"} / app_name + u".ini";
return fs::path{SETTINGS_DIR} / app_name + u".ini";
}
} // namespace
@@ -156,7 +156,7 @@ bool save_settings(std::string_view store_name, const SettingBindings& bindings)
File f;
auto path = get_settings_path(std::string{store_name});
make_new_directory(SETTINGS_DIR);
ensure_directory(SETTINGS_DIR);
auto error = f.create(path);
if (error)
return false;
@@ -50,7 +50,7 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
this->firmware_selected(path);
}});
}
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.ppfw.tar")) {
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.tar")) {
auto filename = entry.path().filename();
auto path = entry.path().native();
@@ -108,7 +108,7 @@ std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::strin
}
void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
if (endsWith(path, u".ppfw.tar")) {
if (endsWith(path, u".tar")) {
// extract, then update
path = extract_tar(u'/' + path).native();
if (path.empty()) return;
-2
View File
@@ -54,8 +54,6 @@
namespace ui {
#define RECON_CFG_FILE u"SETTINGS/recon.cfg"
enum class recon_mode : uint8_t {
Recon,
Scanner,
+58 -2
View File
@@ -27,6 +27,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_touch_calibration.hpp"
#include "ui_text_editor.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
@@ -42,6 +43,7 @@ namespace fs = std::filesystem;
#include "string_format.hpp"
#include "ui_styles.hpp"
#include "cpld_update.hpp"
#include "config_mode.hpp"
namespace pmem = portapack::persistent_memory;
@@ -628,6 +630,58 @@ void SetEncoderDialView::focus() {
button_save.focus();
}
/* AppSettingsView ************************************/
AppSettingsView::AppSettingsView(
NavigationView& nav)
: nav_{nav} {
add_children({&labels,
&menu_view});
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
ensure_directory(SETTINGS_DIR);
for (const auto& entry : std::filesystem::directory_iterator(SETTINGS_DIR, u"*.ini")) {
auto path = (std::filesystem::path)SETTINGS_DIR / entry.path();
menu_view.add_item({path.filename().string().substr(0, 26),
ui::Color::dark_cyan(),
&bitmap_icon_file_text,
[this, path](KeyEvent) {
nav_.push<TextEditorView>(path);
}});
}
}
void AppSettingsView::focus() {
menu_view.focus();
}
/* SetConfigModeView ************************************/
SetConfigModeView::SetConfigModeView(NavigationView& nav) {
add_children({&labels,
&checkbox_config_mode_enabled,
&button_save,
&button_cancel});
checkbox_config_mode_enabled.set_value(!pmem::config_disable_config_mode());
button_save.on_select = [&nav, this](Button&) {
pmem::set_config_disable_config_mode(!checkbox_config_mode_enabled.value());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetConfigModeView::focus() {
button_save.focus();
}
/* SettingsMenuView **************************************/
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
@@ -635,17 +689,19 @@ SettingsMenuView::SettingsMenuView(NavigationView& nav) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
}
add_items({
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<AppSettingsView>(); }},
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav]() { nav.push<SetAudioView>(); }},
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav]() { nav.push<TouchCalibrationView>(); }},
{"Config Mode", ui::Color::dark_cyan(), &bitmap_icon_clk_ext, [&nav]() { nav.push<SetConfigModeView>(); }},
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetConverterSettingsView>(); }},
{"Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav]() { nav.push<SetDateTimeView>(); }},
{"Encoder Dial", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push<SetEncoderDialView>(); }},
{"Freq. Correct", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetFrequencyCorrectionView>(); }},
{"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<SetPersistentMemoryView>(); }},
{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav]() { nav.push<SetQRCodeView>(); }},
{"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetRadioView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav]() { nav.push<SetUIView>(); }},
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav]() { nav.push<SetSDCardView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav]() { nav.push<SetUIView>(); }},
{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav]() { nav.push<SetQRCodeView>(); }},
});
set_max_rows(2); // allow wider buttons
}
+47
View File
@@ -564,6 +564,53 @@ class SetPersistentMemoryView : public View {
};
};
class AppSettingsView : public View {
public:
AppSettingsView(NavigationView& nav);
std::string title() const override { return "App Settings"; };
void focus() override;
private:
NavigationView& nav_;
Labels labels{
{{0, 4}, "Select file to edit:", Color::white()}};
MenuView menu_view{
{0, 2 * 8, 240, 26 * 8},
true};
};
class SetConfigModeView : public View {
public:
SetConfigModeView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Config Mode"; };
private:
Labels labels{
{{1 * 8, 1 * 16}, "Controls whether firmware", Color::light_grey()},
{{1 * 8, 2 * 16}, "will enter Config Mode", Color::light_grey()},
{{1 * 8, 3 * 16}, "after a boot failure.", Color::light_grey()},
};
Checkbox checkbox_config_mode_enabled{
{2 * 8, 6 * 16},
16,
"Config Mode enable"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
};
class SettingsMenuView : public BtnGridView {
public:
SettingsMenuView(NavigationView& nav);
+6 -3
View File
@@ -27,15 +27,18 @@
void config_mode_blink_until_dfu();
void config_mode_set() {
portapack::persistent_memory::set_config_mode_storage(CONFIG_MODE_GUARD_VALUE);
portapack::persistent_memory::set_config_mode_storage_direct(CONFIG_MODE_GUARD_VALUE);
}
bool config_mode_should_enter() {
return portapack::persistent_memory::config_mode_storage() == CONFIG_MODE_GUARD_VALUE;
if (portapack::persistent_memory::config_disable_config_mode_direct())
return false;
else
return portapack::persistent_memory::config_mode_storage_direct() == CONFIG_MODE_GUARD_VALUE;
}
void config_mode_clear() {
portapack::persistent_memory::set_config_mode_storage(CONFIG_MODE_NORMAL_VALUE);
portapack::persistent_memory::set_config_mode_storage_direct(CONFIG_MODE_NORMAL_VALUE);
}
uint32_t blink_patterns[] = {
+2
View File
@@ -31,6 +31,8 @@
void config_mode_set();
bool config_mode_should_enter();
void config_mode_clear();
void config_mode_enable(bool v);
bool config_mode_disabled();
void config_mode_run();
+1 -1
View File
@@ -275,7 +275,7 @@ bool memory_dump(uint32_t* addr_start, uint32_t num_words, bool stack_flag) {
int n{0};
bool data_found{false};
make_new_directory(debug_dir);
ensure_directory(debug_dir);
filename = next_filename_matching_pattern(debug_dir + "/" + (stack_flag ? "STACK" : "MEMORY") + "_DUMP_????.TXT");
error = filename.empty();
if (!error)
+6 -3
View File
@@ -164,9 +164,8 @@ void EventDispatcher::dispatch(const eventmask_t events) {
handle_rtc_tick();
}
if (events & EVT_MASK_USB) {
handle_usb();
}
handle_usb_transfer();
handle_usb();
if (events & EVT_MASK_SWITCHES) {
handle_switches();
@@ -225,6 +224,10 @@ void EventDispatcher::handle_usb() {
portapack::usb_serial.dispatch();
}
void EventDispatcher::handle_usb_transfer() {
portapack::usb_serial.dispatch_transfer();
}
void EventDispatcher::handle_shell() {
if (waiting_for_shellmode) {
waiting_for_shellmode = false;
+1
View File
@@ -122,6 +122,7 @@ class EventDispatcher {
void handle_local_queue();
void handle_rtc_tick();
void handle_usb();
void handle_usb_transfer();
void handle_shell();
static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event);
+1 -1
View File
@@ -177,7 +177,7 @@ enum class PortaPackModel {
static bool save_config(int8_t value) {
persistent_memory::set_config_cpld(value);
if (sd_card::status() == sd_card::Status::Mounted) {
make_new_directory("/hardware");
ensure_directory("/hardware");
File file;
auto sucess = file.create("/hardware/settings.txt");
if (!sucess.is_valid()) {
+228 -147
View File
@@ -65,6 +65,7 @@ GeoPos::GeoPos(
set_lon(0);
const auto changed_fn = [this](int32_t) {
// Convert degrees/minutes/seconds fields to decimal (floating point) lat/lon degree
float lat_value = lat();
float lon_value = lon();
@@ -84,6 +85,27 @@ GeoPos::GeoPos(
field_lon_minutes.on_change = changed_fn;
field_lon_seconds.on_change = changed_fn;
const auto wrapped_lat_seconds = [this](int32_t v) {
field_lat_minutes.on_encoder(v);
};
const auto wrapped_lat_minutes = [this](int32_t v) {
field_lat_degrees.on_encoder((field_lat_degrees.value() >= 0) ? v : -v);
};
const auto wrapped_lon_seconds = [this](int32_t v) {
field_lon_minutes.on_encoder(v);
};
const auto wrapped_lon_minutes = [this](int32_t v) {
field_lon_degrees.on_encoder((field_lon_degrees.value() >= 0) ? v : -v);
};
field_lat_seconds.on_wrap = wrapped_lat_seconds;
field_lat_minutes.on_wrap = wrapped_lat_minutes;
field_lon_seconds.on_wrap = wrapped_lon_seconds;
field_lon_minutes.on_wrap = wrapped_lon_minutes;
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
if (speed_unit_ == KMPH) text_speed_unit.set("kmph");
if (speed_unit_ == MPH) text_speed_unit.set("mph");
@@ -173,12 +195,8 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
if (map_zoom == -2) {
map_zoom = 1;
} else {
map_zoom++;
// When zooming in, ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
if ((map_zoom > 1) && (geomap_rect_width % map_zoom != 0)) {
map_zoom--;
}
// zoom in faster after exceeding the map resolution limit
map_zoom += (map_zoom >= MAP_ZOOM_RESOLUTION_LIMIT) ? map_zoom : 1;
}
}
} else if (delta < 0) {
@@ -186,15 +204,21 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
if (map_zoom == 1) {
map_zoom = -2;
} else {
map_zoom--;
if (map_zoom > MAP_ZOOM_RESOLUTION_LIMIT)
map_zoom /= 2;
else
map_zoom--;
}
}
} else {
return false;
}
map_visible = map_opened && (map_zoom <= MAP_ZOOM_RESOLUTION_LIMIT);
zoom_pixel_offset = (map_visible && (map_zoom > 1)) ? (float)map_zoom / 2 : 0.0f;
// Trigger map redraw
markerListUpdated = true;
redraw_map = true;
set_dirty();
return true;
}
@@ -208,7 +232,9 @@ void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
// Zoom in: Expand each pixel to "map_zoom" number of pixels.
// Future TODO: Add dithering to smooth out the pixelation.
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
// For 240 width, than means no check is needed for map_zoom values up to 6
// For 240 width, than means no check is needed for map_zoom values up to 6.
// (Rectangle height must also divide evenly into map_zoom or we get black lines at end of screen)
// Note that zooming in results in a map offset of (1/map_zoom) pixels to the right & downward directions (see zoom_pixel_offset).
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
for (int j = 0; j < map_zoom; j++) {
buffer[(i * map_zoom) + j] = buffer[i];
@@ -228,91 +254,152 @@ void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
}
void GeoMap::draw_markers(Painter& painter) {
for (int i = 0; i < markerListLen; ++i) {
draw_marker_item(painter, markerList[i], Color::blue(), Color::blue(), Color::magenta());
}
}
void GeoMap::draw_marker_item(Painter& painter, GeoMarker& item, const Color color, const Color fontColor, const Color backColor) {
const auto r = screen_rect();
const ui::Point itemPoint = item_rect_pixel(item);
if ((itemPoint.x() >= 0) && (itemPoint.x() < r.width()) &&
(itemPoint.y() > 10) && (itemPoint.y() < r.height())) // Dont draw within symbol size of top
{
draw_marker(painter, {itemPoint.x(), itemPoint.y() + r.top()}, item.angle, item.tag, color, fontColor, backColor);
}
}
// Calculate screen position of item, adjusted for zoom factor.
ui::Point GeoMap::item_rect_pixel(GeoMarker& item) {
const auto r = screen_rect();
const auto geomap_rect_half_width = r.width() / 2;
const auto geomap_rect_half_height = r.height() / 2;
GeoPoint mapPoint = lat_lon_to_map_pixel(item.lat, item.lon);
float x = mapPoint.x - x_pos;
float y = mapPoint.y - y_pos;
if (map_zoom > 1) {
x = x * map_zoom + zoom_pixel_offset;
y = y * map_zoom + zoom_pixel_offset;
} else if (map_zoom < 0) {
x = x / (-map_zoom);
y = y / (-map_zoom);
}
x += geomap_rect_half_width;
y += geomap_rect_half_height;
return {(int16_t)x, (int16_t)y};
}
// Converts latitude/longitude to pixel coordinates in map file.
// (Note that when map_zoom==1, one pixel in map file corresponds to 1 pixel on screen)
GeoPoint GeoMap::lat_lon_to_map_pixel(float lat, float lon) {
// Using WGS 84/Pseudo-Mercator projection
float x = (map_width * (lon + 180) / 360);
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
double lat_rad = sin(lat * pi / 180);
float y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset));
return {x, y};
}
// Draw grid in place of map (when zoom-in level is too high).
void GeoMap::draw_map_grid() {
const auto r = screen_rect();
for (int i = 0; i < markerListLen; ++i) {
GeoMarker& item = markerList[i];
double lat_rad = sin(item.lat * pi / 180);
int x = (map_width * (item.lon + 180) / 360) - x_pos;
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
// Grid spacing is just based on zoom at the moment, and centered on screen.
// TODO: Maybe align with latitude/longitude seconds instead?
int grid_spacing = map_zoom * 2;
int x = (r.width() / 2) % grid_spacing;
int y = (r.height() / 2) % grid_spacing;
if (map_zoom > 1) {
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2);
y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2);
} else if (map_zoom < 0) {
x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2);
y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2);
}
if (map_zoom <= MAP_ZOOM_RESOLUTION_LIMIT)
return;
if ((x >= 0) && (x < r.width()) &&
(y > 10) && (y < r.height())) // Dont draw within symbol size of top
{
ui::Point itemPoint(x, y + r.top());
if (y >= 32) { // Dont draw text if it would overlap top
// Text and symbol
draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta());
} else {
// Only symbol
draw_bearing(itemPoint, item.angle, 10, Color::blue());
}
}
display.fill_rectangle({{0, r.top()}, {r.width(), r.height()}}, Color::black());
for (uint16_t line = y; line < r.height(); line += grid_spacing) {
display.fill_rectangle({{0, r.top() + line}, {r.width(), 1}}, Color::darker_grey());
}
for (uint16_t column = x; column < r.width(); column += grid_spacing) {
display.fill_rectangle({{column, r.top()}, {1, r.height()}}, Color::darker_grey());
}
}
void GeoMap::paint(Painter& painter) {
const auto r = screen_rect();
std::array<ui::Color, 240> map_line_buffer;
std::array<ui::Color, geomap_rect_width> map_line_buffer;
int16_t zoom_seek_x, zoom_seek_y;
// Ony redraw map if it moved by at least 1 pixel
// or the markers list was updated
int x_diff = abs(x_pos - prev_x_pos);
int y_diff = abs(y_pos - prev_y_pos);
// Ony redraw map if it moved by at least 1 pixel or the markers list was updated
if (map_zoom <= 1) {
// Zooming out, or no zoom
const int min_diff = abs(map_zoom);
if ((int)abs(x_pos - prev_x_pos) >= min_diff)
redraw_map = true;
else if ((int)abs(y_pos - prev_y_pos) >= min_diff)
redraw_map = true;
} else {
// Zooming in; magnify position differences (utilizing zoom_pixel_offset)
if ((int)(abs(x_pos - prev_x_pos) * map_zoom) >= 1)
redraw_map = true;
else if ((int)(abs(y_pos - prev_y_pos) * map_zoom) >= 1)
redraw_map = true;
}
if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) {
int32_t zoom_seek_x = x_pos;
int32_t zoom_seek_y = y_pos;
// Adjust starting corner position of map per zoom setting
if (map_zoom > 1) {
zoom_seek_x += (r.width() - (r.width() / map_zoom)) / 2;
zoom_seek_y += (r.height() - (r.height() / map_zoom)) / 2;
} else if (map_zoom < 0) {
zoom_seek_x += (r.width() - (r.width() * (-map_zoom))) / 2;
zoom_seek_y += (r.height() - (r.height() * (-map_zoom))) / 2;
}
// Read from map file and display to zoomed scale
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
map_read_line(map_line_buffer.data(), r.width());
for (uint16_t j = 0; j < duplicate_lines; j++) {
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
}
}
prev_x_pos = x_pos;
if (redraw_map) {
prev_x_pos = x_pos; // Note x_pos/y_pos pixel position in map file now correspond to screen rect CENTER pixel
prev_y_pos = y_pos;
redraw_map = false;
// Adjust starting corner position of map per zoom setting;
// When zooming in the map should technically by shifted left & up by another map_zoom/2 pixels but
// the map_read_line() function doesn't handle that yet so we're adjusting markers instead (see zoom_pixel_offset).
if (map_zoom > 1) {
zoom_seek_x = x_pos - (float)r.width() / (2 * map_zoom);
zoom_seek_y = y_pos - (float)r.height() / (2 * map_zoom);
} else {
zoom_seek_x = x_pos - (r.width() * abs(map_zoom)) / 2;
zoom_seek_y = y_pos - (r.height() * abs(map_zoom)) / 2;
}
if (map_visible) {
// Read from map file and display to zoomed scale
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
map_read_line(map_line_buffer.data(), r.width());
for (uint16_t j = 0; j < duplicate_lines; j++) {
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
}
}
} else {
// No map data or excessive zoom; just draw a grid
draw_map_grid();
}
// Draw crosshairs in center in manual panning mode
if (manual_panning_) {
display.fill_rectangle({r.center() - Point(16, 1), {32, 2}}, Color::red());
display.fill_rectangle({r.center() - Point(1, 16), {2, 32}}, Color::red());
display.fill_rectangle({r.center() - Point(16, 1) + Point(zoom_pixel_offset, zoom_pixel_offset), {32, 2}}, Color::red());
display.fill_rectangle({r.center() - Point(1, 16) + Point(zoom_pixel_offset, zoom_pixel_offset), {2, 32}}, Color::red());
}
// Draw the other markers
draw_markers(painter);
draw_scale(painter);
draw_mypos();
markerListUpdated = false;
draw_mypos(painter);
set_clean();
}
// Draw the marker in the center
if (!manual_panning_) {
draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black());
if (!manual_panning_ && !hide_center_marker_) {
draw_marker(painter, r.center() + Point(zoom_pixel_offset, zoom_pixel_offset), angle_, tag_, Color::red(), Color::white(), Color::black());
}
}
@@ -336,35 +423,41 @@ bool GeoMap::on_touch(const TouchEvent event) {
}
void GeoMap::move(const float lon, const float lat) {
const auto r = screen_rect();
lon_ = lon;
lat_ = lat;
// Using WGS 84/Pseudo-Mercator projection
x_pos = map_width * (lon_ + 180) / 360 - (geomap_rect_width / 2);
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
double lat_rad = sin(lat * pi / 180);
y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI
// Calculate x_pos/y_pos in map file corresponding to CENTER pixel of screen rect
// (Note there is a 1:1 correspondence between map file pixels and screen pixels when map_zoom=1)
GeoPoint mapPoint = lat_lon_to_map_pixel(lat_, lon_);
x_pos = mapPoint.x;
y_pos = mapPoint.y;
// Cap position
if (x_pos > (map_width - geomap_rect_width))
x_pos = map_width - geomap_rect_width;
if (y_pos > (map_height + geomap_rect_height))
y_pos = map_height - geomap_rect_height;
if (x_pos > (map_width - r.width() / 2))
x_pos = map_width - r.width() / 2;
if (y_pos > (map_height + r.height() / 2))
y_pos = map_height - r.height() / 2;
// Scale calculation
float km_per_deg_lon = cos(lat * pi / 180) * 111.321; // 111.321 km/deg longitude at equator, and 0 km at poles
pixels_per_km = (geomap_rect_width / 2) / km_per_deg_lon;
pixels_per_km = (r.width() / 2) / km_per_deg_lon;
}
bool GeoMap::init() {
auto result = map_file.open("ADSB/world_map.bin");
if (result.is_valid())
return false;
map_opened = !result.is_valid();
map_file.read(&map_width, 2);
map_file.read(&map_height, 2);
if (map_opened) {
map_file.read(&map_width, 2);
map_file.read(&map_height, 2);
} else {
map_width = 32768;
map_height = 32768;
}
map_visible = map_opened;
map_center_x = map_width >> 1;
map_center_y = map_height >> 1;
@@ -375,7 +468,7 @@ bool GeoMap::init() {
map_world_lon = map_width / (2 * pi);
map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
return true;
return map_opened;
}
void GeoMap::set_mode(GeoMapMode mode) {
@@ -391,21 +484,34 @@ bool GeoMap::manual_panning() {
}
void GeoMap::draw_scale(Painter& painter) {
uint16_t km = 800;
uint16_t scale_width = (map_zoom > 0) ? km * pixels_per_km * map_zoom : km * pixels_per_km / (-map_zoom);
const auto r = screen_rect();
uint32_t m = 800000;
uint32_t scale_width = (map_zoom > 0) ? m * map_zoom * pixels_per_km : m * pixels_per_km / (-map_zoom);
ui::Color scale_color = (map_visible) ? Color::black() : Color::white();
std::string km_string;
while (scale_width > screen_width / 2) {
while (scale_width > (uint32_t)r.width() * (1000 / 2)) {
scale_width /= 2;
km /= 2;
m /= 2;
}
scale_width /= 1000;
if (m < 1000) {
km_string = to_string_dec_uint(m) + "m";
} else {
uint32_t km = m / 1000;
m -= km * 1000;
if (m == 0) {
km_string = to_string_dec_uint(km) + " km";
} else {
km_string = to_string_dec_uint(km) + "." + to_string_dec_uint((m + 50) / 100, 1) + "km";
}
}
std::string km_string = to_string_dec_uint(km) + " km";
display.fill_rectangle({{r.right() - 5 - (uint16_t)scale_width, r.bottom() - 4}, {(uint16_t)scale_width, 2}}, scale_color);
display.fill_rectangle({{r.right() - 5, r.bottom() - 8}, {2, 6}}, scale_color);
display.fill_rectangle({{r.right() - 5 - (uint16_t)scale_width, r.bottom() - 8}, {2, 6}}, scale_color);
display.fill_rectangle({{screen_width - 5 - scale_width, screen_height - 4}, {scale_width, 2}}, Color::black());
display.fill_rectangle({{screen_width - 5, screen_height - 8}, {2, 6}}, Color::black());
display.fill_rectangle({{screen_width - 5 - scale_width, screen_height - 8}, {2, 6}}, Color::black());
painter.draw_string({(uint16_t)(screen_width - 25 - scale_width - km_string.length() * 5 / 2), screen_height - 10}, ui::font::fixed_5x8, Color::black(), Color::white(), km_string);
painter.draw_string({(uint16_t)(r.right() - 25 - scale_width - km_string.length() * 5 / 2), r.bottom() - 10}, ui::font::fixed_5x8, Color::black(), Color::white(), km_string);
}
void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color) {
@@ -422,9 +528,13 @@ void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t siz
size--;
}
display.draw_pixel(origin, color); // 1 pixel indicating center pivot point of bearing symbol
}
void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color, const Color fontColor, const Color backColor) {
const auto r = screen_rect();
int tagOffset = 10;
if (mode_ == PROMPT) {
// Cross
@@ -442,45 +552,15 @@ void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint
tagOffset = 8;
}
// center tag above point
if (itemTag.find_first_not_of(' ') != itemTag.npos) { // only draw tag if we have something other than spaces
if ((itemPoint.y() - r.top() >= 32) && (itemTag.find_first_not_of(' ') != itemTag.npos)) { // only draw tag if doesn't overlap top & not just spaces
painter.draw_string(itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset),
style().font, fontColor, backColor, itemTag);
}
}
void GeoMap::draw_mypos() {
const auto r = screen_rect();
if (my_lat >= 200 || my_lon >= 200) return; // invalid
int x = (map_width * (my_lon + 180) / 360) - x_pos;
double lat_rad = sin(my_lat * pi / 180);
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
auto color = Color::yellow();
if (map_zoom > 1) {
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2);
y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2);
} else if (map_zoom < 0) {
x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2);
y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2);
}
if ((x >= 0) && (x < r.width()) &&
(y > 10) && (y < r.height())) // Dont draw within symbol size of top
{
ui::Point itemPoint(x, y + r.top());
if (mode_ == PROMPT) {
// Cross
display.fill_rectangle({itemPoint - Point(16, 1), {32, 2}}, color);
display.fill_rectangle({itemPoint - Point(1, 16), {2, 32}}, color);
} else if (my_angle < 360) {
// if we have a valid angle draw bearing
draw_bearing(itemPoint, my_angle, 10, color);
} else {
// draw a small cross
display.fill_rectangle({itemPoint - Point(8, 1), {16, 2}}, color);
display.fill_rectangle({itemPoint - Point(1, 8), {2, 16}}, color);
}
}
void GeoMap::draw_mypos(Painter& painter) {
if ((my_pos.lat < INVALID_LAT_LON) && (my_pos.lon < INVALID_LAT_LON))
draw_marker_item(painter, my_pos, Color::yellow());
}
void GeoMap::clear_markers() {
@@ -488,20 +568,22 @@ void GeoMap::clear_markers() {
}
MapMarkerStored GeoMap::store_marker(GeoMarker& marker) {
const auto r = screen_rect();
MapMarkerStored ret;
// Check if it could be on screen
// Only checking one direction to reduce CPU
double lat_rad = sin(marker.lat * pi / 180);
int x = (map_width * (marker.lon + 180) / 360) - x_pos;
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
if (false == ((x >= 0) && (x < geomap_rect_width) && (y > 10) && (y < geomap_rect_height))) // Dont draw within symbol size of top
{
// (Shows more distant planes when zoomed out)
GeoPoint mapPoint = lat_lon_to_map_pixel(marker.lat, marker.lon);
int x_dist = abs((int)mapPoint.x - (int)x_pos);
int y_dist = abs((int)mapPoint.y - (int)y_pos);
int zoom_out = (map_zoom < 0) ? -map_zoom : 1;
if ((x_dist >= (zoom_out * r.width() / 2)) || (y_dist >= (zoom_out * r.height() / 2))) {
ret = MARKER_NOT_STORED;
} else if (markerListLen < NumMarkerListElements) {
markerList[markerListLen] = marker;
markerListLen++;
markerListUpdated = true;
redraw_map = true;
ret = MARKER_STORED;
} else {
ret = MARKER_LIST_FULL;
@@ -510,16 +592,17 @@ MapMarkerStored GeoMap::store_marker(GeoMarker& marker) {
}
void GeoMap::update_my_position(float lat, float lon, int32_t altitude) {
my_lat = lat;
my_lon = lon;
my_pos.lat = lat;
my_pos.lon = lon;
my_altitude = altitude;
markerListUpdated = true;
redraw_map = true;
set_dirty();
}
void GeoMap::update_my_orientation(uint16_t angle, bool refresh) {
my_angle = angle;
my_pos.angle = angle;
if (refresh) {
markerListUpdated = true;
redraw_map = true;
set_dirty();
}
}
@@ -527,7 +610,7 @@ void GeoMap::update_my_orientation(uint16_t angle, bool refresh) {
void GeoMapView::focus() {
geopos.focus();
if (!map_opened)
if (!geomap.map_file_opened())
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT);
}
@@ -627,8 +710,7 @@ GeoMapView::GeoMapView(
add_child(&geopos);
map_opened = geomap.init();
if (!map_opened) return;
geomap.init();
setup();
@@ -660,8 +742,7 @@ GeoMapView::GeoMapView(
add_child(&geopos);
map_opened = geomap.init();
if (!map_opened) return;
geomap.init();
setup();
add_child(&button_ok);
+49 -20
View File
@@ -29,16 +29,30 @@
#include "portapack.hpp"
#define MAX_MAP_ZOOM_IN 5
#define MAX_MAP_ZOOM_OUT 10
namespace ui {
#define MAX_MAP_ZOOM_IN 4000
#define MAX_MAP_ZOOM_OUT 10
#define MAP_ZOOM_RESOLUTION_LIMIT 5 // Max zoom-in to show map; rect height & width must divide into this evenly
#define INVALID_LAT_LON 200
#define INVALID_ANGLE 400
#define GEOMAP_BANNER_HEIGHT (3 * 16)
#define GEOMAP_RECT_WIDTH 240
#define GEOMAP_RECT_HEIGHT (320 - 16 - GEOMAP_BANNER_HEIGHT)
enum GeoMapMode {
DISPLAY,
PROMPT
};
struct GeoPoint {
public:
float x{0};
float y{0};
};
struct GeoMarker {
public:
float lat{0};
@@ -133,13 +147,15 @@ class GeoPos : public View {
2,
{0, 59},
1,
' '};
' ',
true};
NumberField field_lat_seconds{
{13 * 8, 1 * 16},
2,
{0, 59},
1,
' '};
' ',
true};
Text text_lat_decimal{
{17 * 8, 1 * 16, 13 * 8, 1 * 16},
""};
@@ -155,13 +171,15 @@ class GeoPos : public View {
2,
{0, 59},
1,
' '};
' ',
true};
NumberField field_lon_seconds{
{13 * 8, 2 * 16},
2,
{0, 59},
1,
' '};
' ',
true};
Text text_lon_decimal{
{17 * 8, 2 * 16, 13 * 8, 1 * 16},
""};
@@ -201,26 +219,40 @@ class GeoMap : public Widget {
angle_ = new_angle;
}
bool map_file_opened() { return map_opened; }
void set_hide_center_marker(bool hide) {
hide_center_marker_ = hide;
}
bool hide_center_marker() { return hide_center_marker_; }
static const int NumMarkerListElements = 30;
void clear_markers();
MapMarkerStored store_marker(GeoMarker& marker);
static const Dim banner_height = 3 * 16;
static const Dim geomap_rect_width = 240;
static const Dim geomap_rect_height = 320 - 16 - banner_height;
static const Dim banner_height = GEOMAP_BANNER_HEIGHT;
static const Dim geomap_rect_width = GEOMAP_RECT_WIDTH;
static const Dim geomap_rect_height = GEOMAP_RECT_HEIGHT;
private:
void draw_scale(Painter& painter);
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
ui::Point item_rect_pixel(GeoMarker& item);
GeoPoint lat_lon_to_map_pixel(float lat, float lon);
void draw_marker_item(Painter& painter, GeoMarker& item, const Color color, const Color fontColor = Color::white(), const Color backColor = Color::black());
void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black());
void draw_markers(Painter& painter);
void draw_mypos();
void draw_mypos(Painter& painter);
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
void draw_map_grid();
void map_read_line(ui::Color* buffer, uint16_t pixels);
bool manual_panning_{false};
bool hide_center_marker_{false};
GeoMapMode mode_{};
File map_file{};
bool map_opened{};
bool map_visible{};
uint16_t map_width{}, map_height{};
int32_t map_center_x{}, map_center_y{};
int16_t map_zoom{1};
@@ -229,23 +261,22 @@ class GeoMap : public Widget {
double map_world_lon{};
double map_offset{};
int32_t x_pos{}, y_pos{};
int32_t prev_x_pos{0xFFFF}, prev_y_pos{0xFFFF};
float x_pos{}, y_pos{};
float prev_x_pos{32767.0f}, prev_y_pos{32767.0f};
float lat_{};
float lon_{};
float zoom_pixel_offset{0.0f};
float pixels_per_km{};
uint16_t angle_{};
std::string tag_{};
// the portapack's position data ( for example injected from serial )
float my_lat{200};
float my_lon{200};
GeoMarker my_pos{INVALID_LAT_LON, INVALID_LAT_LON, INVALID_ANGLE, ""}; // lat, lon, angle, tag
int32_t my_altitude{0};
uint16_t my_angle{400};
int markerListLen{0};
GeoMarker markerList[NumMarkerListElements];
bool markerListUpdated{false};
bool redraw_map{false};
};
class GeoMapView : public View {
@@ -303,8 +334,6 @@ class GeoMapView : public View {
uint16_t angle_{};
std::function<void(void)> on_close_{nullptr};
bool map_opened{};
GeoPos geopos{
{0, 0},
altitude_unit_,
+10 -2
View File
@@ -146,7 +146,7 @@ std::vector<AppInfoConsole> NavigationView::fixedAppListFC = {
{"lookingglass", "Looking Glass", RX}};
bool NavigationView::StartAppByName(const char* name) {
pop();
home(false);
if (strcmp(name, "adsbrx") == 0) {
push<ADSBRxView>();
return true;
@@ -570,7 +570,7 @@ void SystemStatusView::rtc_battery_workaround() {
}
}
} else {
make_new_directory(SETTINGS_DIR);
ensure_directory(SETTINGS_DIR);
make_new_file(DATE_FILEFLAG);
year = 1980;
@@ -660,6 +660,14 @@ void NavigationView::pop(bool trigger_update) {
if (on_pop) on_pop();
}
void NavigationView::home(bool trigger_update) {
while (view_stack.size() > 1) {
pop(false);
}
if (trigger_update) update_view();
}
void NavigationView::display_modal(
const std::string& title,
const std::string& message) {
+1
View File
@@ -91,6 +91,7 @@ class NavigationView : public View {
View* push_view(std::unique_ptr<View> new_view);
void replace(View* v);
void pop(bool trigger_update = true);
void home(bool trigger_update);
void display_modal(const std::string& title, const std::string& message);
void display_modal(
+8 -2
View File
@@ -1,11 +1,12 @@
extern "C" {
#include "usb_serial_io.h"
#include "usb_serial_device_to_host.h"
#include "usb_serial_cdc.h"
}
#include "usb_serial_shell.hpp"
#include "usb_serial.hpp"
#include "portapack.hpp"
#include "usb_serial_host_to_device.hpp"
#include <libopencm3/cm3/common.h>
#include <libopencm3/lpc43xx/usb.h>
@@ -24,6 +25,7 @@ void USBSerial::initialize() {
init_serial_usb_driver(&SUSBD1);
shellInit();
init_host_to_device();
}
void USBSerial::dispatch() {
@@ -35,7 +37,11 @@ void USBSerial::dispatch() {
create_shell(_eventDispatcher);
}
bulk_out_receive();
schedule_host_to_device_transfer();
}
void USBSerial::dispatch_transfer() {
complete_host_to_device_transfer();
}
void USBSerial::on_channel_opened() {
+1
View File
@@ -32,6 +32,7 @@ class USBSerial {
public:
void initialize();
void dispatch();
void dispatch_transfer();
void on_channel_opened();
void on_channel_closed();
void setEventDispatcher(EventDispatcher* ed) { _eventDispatcher = ed; }
+8 -3
View File
@@ -31,11 +31,16 @@ static Thread* thread_usb_event = NULL;
CH_IRQ_HANDLER(USB0_IRQHandler) {
CH_IRQ_PROLOGUE();
const uint32_t status = USB0_USBSTS_D & USB0_USBINTR_D;
usb0_isr();
chSysLockFromIsr();
chEvtSignalI(thread_usb_event, EVT_MASK_USB);
chSysUnlockFromIsr();
if (status & USB0_USBSTS_D_UI) {
chSysLockFromIsr();
chEvtSignalI(thread_usb_event, EVT_MASK_USB);
chSysUnlockFromIsr();
}
CH_IRQ_EPILOGUE();
}
@@ -19,7 +19,7 @@
* Boston, MA 02110-1301, USA.
*/
#include "usb_serial_io.h"
#include "usb_serial_device_to_host.h"
#include "usb_serial_endpoints.h"
@@ -40,39 +40,6 @@
SerialUSBDriver SUSBD1;
uint8_t usbBulkBuffer[USBSERIAL_BUFFERS_SIZE];
void bulk_out_receive(void) {
int ret;
do {
ret = usb_transfer_schedule(
&usb_endpoint_bulk_out,
&usbBulkBuffer[0],
USBSERIAL_BUFFERS_SIZE,
serial_bulk_transfer_complete,
NULL);
} while (ret != -1);
}
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) {
(void)user_data;
chSysLockFromIsr();
for (unsigned int i = 0; i < bytes_transferred; i++) {
msg_t ret;
do {
ret = chIQPutI(&SUSBD1.iqueue, usbBulkBuffer[i]);
if (ret == Q_FULL)
chThdSleepMilliseconds(1);
} while (ret == Q_FULL);
}
chSysUnlockFromIsr();
}
static void onotify(GenericQueue* qp) {
SerialUSBDriver* sdp = chQGetLink(qp);
uint8_t buff[USBSERIAL_BUFFERS_SIZE];
@@ -24,8 +24,10 @@
#include "ch.h"
#include "hal.h"
#define USB_BULK_BUFFER_SIZE 64
#ifndef USBSERIAL_BUFFERS_SIZE
#define USBSERIAL_BUFFERS_SIZE 400
#define USBSERIAL_BUFFERS_SIZE 128
#endif
struct SerialUSBDriverVMT {
@@ -46,8 +48,6 @@ typedef struct SerialUSBDriver SerialUSBDriver;
extern SerialUSBDriver SUSBD1;
void init_serial_usb_driver(SerialUSBDriver* sdp);
void bulk_out_receive(void);
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
#ifdef __cplusplus
extern "C" {
@@ -0,0 +1,122 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "usb_serial_host_to_device.hpp"
#include "event_m0.hpp"
#include "usb_serial_device_to_host.h"
extern "C" {
#include <common/usb.h>
#include <hackrf_usb/usb_device.h>
#include <hackrf_usb/usb_endpoint.h>
}
#include <queue>
#include <vector>
static Thread* thread_usb_event = NULL;
struct usb_bulk_buffer_t {
uint8_t* data;
size_t length;
bool completed;
};
std::queue<usb_bulk_buffer_t*> usb_bulk_buffer_queue;
std::queue<usb_bulk_buffer_t*> usb_bulk_buffer_spare;
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) {
usb_bulk_buffer_t* transfer_data = (usb_bulk_buffer_t*)user_data;
transfer_data->length = bytes_transferred;
transfer_data->completed = true;
}
void init_host_to_device() {
thread_usb_event = chThdSelf();
}
void schedule_host_to_device_transfer() {
if (usb_bulk_buffer_queue.size() >= 8)
return;
static usb_bulk_buffer_t* transfer_data = nullptr;
int ret;
do {
if (transfer_data == nullptr) {
if (usb_bulk_buffer_spare.empty() == false) {
transfer_data = usb_bulk_buffer_spare.front();
transfer_data->length = 0;
transfer_data->completed = false;
usb_bulk_buffer_spare.pop();
} else {
transfer_data = new usb_bulk_buffer_t{
.data = new uint8_t[USB_BULK_BUFFER_SIZE],
.length = 0,
.completed = false};
}
}
ret = usb_transfer_schedule(
&usb_endpoint_bulk_out,
transfer_data->data,
USB_BULK_BUFFER_SIZE,
serial_bulk_transfer_complete,
transfer_data);
if (ret != -1) {
usb_bulk_buffer_queue.push(transfer_data);
transfer_data = nullptr;
if (usb_bulk_buffer_queue.size() >= 8)
return;
}
} while (ret != -1);
}
void complete_host_to_device_transfer() {
for (; !usb_bulk_buffer_queue.empty(); usb_bulk_buffer_queue.pop()) {
usb_bulk_buffer_t* transfer_data = usb_bulk_buffer_queue.front();
while (transfer_data->completed == false)
return;
chSysLock();
for (unsigned int i = 0; i < transfer_data->length; i++) {
msg_t ret;
do {
ret = chIQPutI(&SUSBD1.iqueue, transfer_data->data[i]);
if (ret == Q_FULL) {
chSysUnlock();
chThdSleepMilliseconds(1); // wait for shell thread when buffer is full
chSysLock();
}
} while (ret == Q_FULL);
}
chSysUnlock();
usb_bulk_buffer_spare.push(transfer_data);
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ch.h"
#include "hal.h"
#define USB_BULK_BUFFER_SIZE 64
void init_host_to_device();
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
void schedule_host_to_device_transfer();
void complete_host_to_device_transfer();
+106 -2
View File
@@ -35,8 +35,9 @@
#include "portapack_cpld_data.hpp"
#include "crc.hpp"
#include "hackrf_cpld_data.hpp"
#include "performance_counter.hpp"
#include "usb_serial_io.h"
#include "usb_serial_device_to_host.h"
#include "chprintf.h"
#include "chqueues.h"
#include "ui_external_items_menu_loader.hpp"
@@ -46,6 +47,8 @@
#include "ui_navigation.hpp"
#include "usb_serial_shell_filesystem.hpp"
#include "portapack_persistent_memory.hpp"
#include <string>
#include <cstring>
#include <libopencm3/lpc43xx/wwdt.h>
@@ -148,7 +151,7 @@ static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
if (!top_widget) return;
auto nav = static_cast<ui::SystemView*>(top_widget)->get_navigation_view();
if (!nav) return;
nav->display_modal("Flashing", "Flashing from serial.\rPlease wait!\nDevice will restart.");
nav->display_modal("Flashing", "Flashing from serial.\r\nPlease wait!\r\nDevice will restart.");
// check file extensions
if (strEndsWith(path.native(), u".ppfw.tar")) {
// extract tar
@@ -916,6 +919,103 @@ static void cmd_gotorientation(BaseSequentialStream* chp, int argc, char* argv[]
chprintf(chp, "ok\r\n");
}
static void cmd_sysinfo(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: sysinfo\r\n";
(void)argv;
if (argc > 0) {
chprintf(chp, usage);
return;
}
auto utilisation = get_cpu_utilisation_in_percent();
std::string info =
"M0 heap: " + to_string_dec_uint(chCoreStatus()) + "\r\n" +
"M0 stack: " + to_string_dec_uint((uint32_t)get_free_stack_space()) + "\r\n" +
"M0 cpu%: " + to_string_dec_uint(utilisation) + "\r\n" +
"M4 heap: " + to_string_dec_uint(shared_memory.m4_heap_usage) + "\r\n" +
"M4 stack: " + to_string_dec_uint(shared_memory.m4_stack_usage) + "\r\n" +
"M0 cpu%: " + to_string_dec_uint(shared_memory.m4_performance_counter) + "\r\n" +
"M4 miss: " + to_string_dec_uint(shared_memory.m4_buffer_missed) + "\r\n" +
"uptime: " + to_string_dec_uint(chTimeNow() / 1000) + "\r\n";
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)info.c_str(), info.length());
return;
}
static void cmd_radioinfo(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: radioinfo\r\n";
(void)argv;
if (argc > 0) {
chprintf(chp, usage);
return;
}
std::string info =
"receiver_model.target_frequency: " + to_string_dec_uint(portapack::receiver_model.target_frequency()) + "\r\n" +
"receiver_model.baseband_bandwidth: " + to_string_dec_uint(portapack::receiver_model.baseband_bandwidth()) + "\r\n" +
"receiver_model.sampling_rate: " + to_string_dec_uint(portapack::receiver_model.sampling_rate()) + "\r\n" +
"receiver_model.modulation: " + to_string_dec_uint((uint32_t)portapack::receiver_model.modulation()) + "\r\n" +
"receiver_model.am_configuration: " + to_string_dec_uint(portapack::receiver_model.am_configuration()) + "\r\n" +
"receiver_model.nbfm_configuration: " + to_string_dec_uint(portapack::receiver_model.nbfm_configuration()) + "\r\n" +
"receiver_model.wfm_configuration: " + to_string_dec_uint(portapack::receiver_model.wfm_configuration()) + "\r\n" +
"transmitter_model.target_frequency: " + to_string_dec_uint(portapack::transmitter_model.target_frequency()) + "\r\n" +
"transmitter_model.baseband_bandwidth: " + to_string_dec_uint(portapack::transmitter_model.baseband_bandwidth()) + "\r\n" +
"transmitter_model.sampling_rate: " + to_string_dec_uint(portapack::transmitter_model.sampling_rate()) + "\r\n";
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)info.c_str(), info.length());
return;
}
static void cmd_pmemreset(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: pmemreset yes\r\nThis will reset pmem to defaults!\r\n";
(void)argv;
if (argc != 1 || strcmp(argv[0], "yes") != 0) {
chprintf(chp, usage);
return;
}
auto evtd = getEventDispatcherInstance();
if (!evtd) return;
auto top_widget = evtd->getTopWidget();
if (!top_widget) return;
auto nav = static_cast<ui::SystemView*>(top_widget)->get_navigation_view();
if (!nav) return;
nav->home(true);
portapack::persistent_memory::cache::defaults();
// system refresh
StatusRefreshMessage message{};
EventDispatcher::send_message(message);
chprintf(chp, "ok\r\n");
}
static void cmd_settingsreset(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: settingsreset yes\r\nThis will reset all app settings to defaults!\r\n";
(void)argv;
if (argc != 1 || strcmp(argv[0], "yes") != 0) {
chprintf(chp, usage);
return;
}
auto evtd = getEventDispatcherInstance();
if (!evtd) return;
auto top_widget = evtd->getTopWidget();
if (!top_widget) return;
auto nav = static_cast<ui::SystemView*>(top_widget)->get_navigation_view();
if (!nav) return;
nav->home(true); // to exit all running apps
for (const auto& entry : std::filesystem::directory_iterator(SETTINGS_DIR, u"*.ini")) {
if (std::filesystem::is_regular_file(entry.status())) {
std::filesystem::path pth = SETTINGS_DIR;
pth += u"/" + entry.path();
chprintf(chp, pth.string().c_str());
chprintf(chp, "\r\n");
f_unlink(pth.tchar());
}
}
// system refresh
StatusRefreshMessage message{};
EventDispatcher::send_message(message);
chprintf(chp, "ok\r\n");
}
static const ShellCommand commands[] = {
{"reboot", cmd_reboot},
{"dfu", cmd_dfu},
@@ -941,6 +1041,10 @@ static const ShellCommand commands[] = {
{"appstart", cmd_appstart},
{"gotgps", cmd_gotgps},
{"gotorientation", cmd_gotorientation},
{"sysinfo", cmd_sysinfo},
{"radioinfo", cmd_radioinfo},
{"pmemreset", cmd_pmemreset},
{"settingsreset", cmd_settingsreset},
{NULL, NULL}};
static const ShellConfig shell_cfg1 = {
@@ -20,7 +20,7 @@
*/
#include "usb_serial_shell_filesystem.hpp"
#include "usb_serial_io.h"
#include "usb_serial_device_to_host.h"
#include "chprintf.h"
#include "string_format.hpp"
@@ -172,6 +172,9 @@ void cmd_sd_close(BaseSequentialStream* chp, int argc, char* argv[]) {
return;
}
auto error = shell_file->sync();
if (report_on_error(chp, error)) return;
delete shell_file;
shell_file = nullptr;
@@ -348,19 +351,23 @@ void cmd_sd_write_binary(BaseSequentialStream* chp, int argc, char* argv[]) {
return;
}
long size = (int)strtol(argv[0], NULL, 10);
size_t size = (size_t)strtol(argv[0], NULL, 10);
chprintf(chp, "send %d bytes\r\n", size);
uint8_t buffer;
uint8_t buffer[USB_BULK_BUFFER_SIZE];
for (long i = 0; i < size; i++) {
if (chSequentialStreamRead(chp, &buffer, 1) == 0)
do {
size_t bytes_to_read = size > USB_BULK_BUFFER_SIZE ? USB_BULK_BUFFER_SIZE : size;
size_t bytes_read = chSequentialStreamRead(chp, &buffer[0], bytes_to_read);
if (bytes_read != bytes_to_read)
return;
auto error = shell_file->write(&buffer, 1);
auto error = shell_file->write(&buffer[0], bytes_read);
if (report_on_error(chp, error)) return;
}
size -= bytes_read;
} while (size > 0);
chprintf(chp, "ok\r\n");
}
+1 -1
View File
@@ -30,7 +30,7 @@
#include "optional.hpp"
#include "chprintf.h"
#include "usb_serial_io.h"
#include "usb_serial_device_to_host.h"
#include <cstdint>
#include <cstddef>
+33 -13
View File
@@ -145,7 +145,7 @@ struct misc_config_t {
bool disable_speaker : 1;
bool config_disable_external_tcxo : 1;
bool config_sdcard_high_speed_io : 1;
bool UNUSED_4 : 1;
bool config_disable_config_mode : 1;
bool UNUSED_5 : 1;
bool UNUSED_6 : 1;
bool UNUSED_7 : 1;
@@ -156,6 +156,8 @@ struct misc_config_t {
};
static_assert(sizeof(misc_config_t) == sizeof(uint32_t));
#define MC_CONFIG_DISABLE_CONFIG_MODE 0x00000010 // config_disable_config_mode bit in struct above
/* IMPORTANT: Update dump_persistent_memory (below) when changing data_t. */
/* Struct must pack the same way on M4 and M0 cores.
@@ -386,6 +388,7 @@ namespace cache {
void defaults() {
cached_backup_ram = backup_ram_t();
// If the desired default is 0/false, then no need to set it here (buffer is initialized to 0)
set_config_backlight_timer(backlight_config_t{});
set_config_splash(true);
set_config_disable_external_tcxo(false);
@@ -416,8 +419,9 @@ void init() {
const auto switches_state = get_switches_state();
// ignore for valid check
auto config_mode_backup = config_mode_storage();
set_config_mode_storage(CONFIG_MODE_NORMAL_VALUE);
auto config_mode_backup = config_mode_storage_direct();
set_config_mode_storage_direct(CONFIG_MODE_NORMAL_VALUE);
if (!(switches_state[(size_t)ui::KeyEvent::Left] && switches_state[(size_t)ui::KeyEvent::Right]) && backup_ram->is_valid()) {
// Copy valid persistent data into cache.
cached_backup_ram = *backup_ram;
@@ -433,7 +437,7 @@ void init() {
// Copy defaults into cache.
defaults();
}
set_config_mode_storage(config_mode_backup);
set_config_mode_storage_direct(config_mode_backup);
}
void persist() {
@@ -597,6 +601,10 @@ bool config_disable_external_tcxo() {
return data->misc_config.config_disable_external_tcxo;
}
bool config_disable_config_mode() {
return data->misc_config.config_disable_config_mode;
}
bool config_sdcard_high_speed_io() {
return data->misc_config.config_sdcard_high_speed_io;
}
@@ -666,6 +674,10 @@ void set_config_disable_external_tcxo(bool v) {
data->misc_config.config_disable_external_tcxo = v;
}
void set_config_disable_config_mode(bool v) {
data->misc_config.config_disable_config_mode = v;
}
void set_config_sdcard_high_speed_io(bool v, bool save) {
if (v) {
/* 200MHz / (2 * 2) = 50MHz */
@@ -947,12 +959,18 @@ void set_encoder_dial_sensitivity(uint8_t v) {
// Recovery mode magic value storage
static data_t* data_direct_access = reinterpret_cast<data_t*>(memory::map::backup_ram.base());
uint32_t config_mode_storage() {
uint32_t config_mode_storage_direct() {
return data_direct_access->config_mode_storage;
}
void set_config_mode_storage(uint32_t v) {
void set_config_mode_storage_direct(uint32_t v) {
data_direct_access->config_mode_storage = v;
}
bool config_disable_config_mode_direct() {
// "return data_direct_access->misc_config.config_disable_config_mode"
// Casting as U32 as workaround for misaligned memory access
uint32_t misc_config_u32 = *(uint32_t*)&data_direct_access->misc_config;
return ((misc_config_u32 & MC_CONFIG_DISABLE_CONFIG_MODE) != 0);
}
// PMem to sdcard settings
@@ -963,7 +981,7 @@ bool should_use_sdcard_for_pmem() {
int save_persistent_settings_to_file() {
File outfile;
make_new_directory(SETTINGS_DIR);
ensure_directory(SETTINGS_DIR);
auto error = outfile.create(PMEM_SETTING_FILE);
if (error)
return false;
@@ -996,7 +1014,7 @@ bool debug_dump() {
std::filesystem::path filename{};
File pmem_dump_file{};
// create new dump file name and DEBUG directory
make_new_directory(debug_dir);
ensure_directory(debug_dir);
filename = next_filename_matching_pattern(debug_dir + "/DEBUG_DUMP_????.TXT");
if (filename.empty()) {
painter.draw_string({0, 320 - 16}, ui::Styles::red, "COULD NOT GET DUMP NAME !");
@@ -1037,9 +1055,9 @@ bool debug_dump() {
pmem_dump_file.write_line("tone_mix: " + to_string_dec_uint(data->tone_mix));
pmem_dump_file.write_line("hardware_config: " + to_string_dec_uint(data->hardware_config));
pmem_dump_file.write_line("recon_config: 0x" + to_string_hex(data->recon_config, 16));
pmem_dump_file.write_line("recon_repeat_nb: " + to_string_dec_int(data->recon_repeat_nb, 16));
pmem_dump_file.write_line("recon_repeat_gain:" + to_string_hex(data->recon_config, 16));
pmem_dump_file.write_line("recon_repeat_delay:" + to_string_hex(data->recon_config, 16));
pmem_dump_file.write_line("recon_repeat_nb: " + to_string_dec_int(data->recon_repeat_nb));
pmem_dump_file.write_line("recon_repeat_gain: " + to_string_dec_int(data->recon_repeat_gain));
pmem_dump_file.write_line("recon_repeat_delay: " + to_string_dec_int(data->recon_repeat_delay));
pmem_dump_file.write_line("converter: " + to_string_dec_int(data->converter));
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->updown_converter));
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(data->updown_frequency_rx_correction));
@@ -1054,6 +1072,7 @@ bool debug_dump() {
pmem_dump_file.write_line("encoder_dial_sensitivity: " + to_string_dec_uint(data->encoder_dial_sensitivity));
// pmem_dump_file.write_line("UNUSED_8: " + to_string_dec_uint(data->UNUSED_8));
pmem_dump_file.write_line("headphone_volume_cb: " + to_string_dec_int(data->headphone_volume_cb));
pmem_dump_file.write_line("config_mode_storage: 0x" + to_string_hex(data->config_mode_storage, 8));
// ui_config bits
const auto backlight_timer = portapack::persistent_memory::config_backlight_timer();
@@ -1086,8 +1105,9 @@ bool debug_dump() {
// misc_config bits
pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute()));
pmem_dump_file.write_line("misc_config config_speaker_disable: " + to_string_dec_int(config_speaker_disable()));
pmem_dump_file.write_line("ui_config config_disable_external_tcxo: " + to_string_dec_uint(config_disable_external_tcxo()));
pmem_dump_file.write_line("ui_config config_sdcard_high_speed_io: " + to_string_dec_uint(config_sdcard_high_speed_io()));
pmem_dump_file.write_line("misc_config config_disable_external_tcxo: " + to_string_dec_uint(config_disable_external_tcxo()));
pmem_dump_file.write_line("misc_config config_sdcard_high_speed_io: " + to_string_dec_uint(config_sdcard_high_speed_io()));
pmem_dump_file.write_line("misc_config config_disable_config_mode: " + to_string_dec_uint(config_disable_config_mode()));
// receiver_model
pmem_dump_file.write_line("\n[Receiver Model]");
@@ -33,10 +33,10 @@
#include "serializer.hpp"
#include "volume.hpp"
// persistant memory from/to sdcard flag file
// persistent memory from/to sdcard flag file
#define PMEM_FILEFLAG u"/SETTINGS/PMEM_FILEFLAG"
// persistant memory from/to sdcard flag file
// persistent memory from/to sdcard flag file
#define PMEM_SETTING_FILE u"/SETTINGS/pmem_settings"
#define PMEM_SIZE_BYTES 256 // total amount of pmem space in bytes, including checksum
@@ -178,6 +178,8 @@ void set_config_cpld(uint8_t i);
bool config_disable_external_tcxo();
bool config_sdcard_high_speed_io();
bool config_disable_config_mode();
bool config_splash();
bool config_converter();
bool config_updown_converter();
@@ -198,6 +200,8 @@ void set_save_app_settings(bool v);
void set_show_bigger_qr_code(bool v);
void set_config_disable_external_tcxo(bool v);
void set_config_sdcard_high_speed_io(bool v, bool save);
void set_config_disable_config_mode(bool v);
void set_config_splash(bool v);
bool config_converter();
bool config_updown_converter();
@@ -223,10 +227,11 @@ void set_disable_touchscreen(bool v);
uint8_t config_encoder_dial_sensitivity();
void set_encoder_dial_sensitivity(uint8_t v);
#define CONFIG_MODE_GUARD_VALUE 2001
#define CONFIG_MODE_NORMAL_VALUE 1999
uint32_t config_mode_storage();
void set_config_mode_storage(uint32_t v);
#define CONFIG_MODE_GUARD_VALUE 0x000007d1
#define CONFIG_MODE_NORMAL_VALUE 0x000007cf
uint32_t config_mode_storage_direct();
void set_config_mode_storage_direct(uint32_t v);
bool config_disable_config_mode_direct();
uint32_t pocsag_last_address();
void set_pocsag_last_address(uint32_t address);
+9 -1
View File
@@ -31,7 +31,7 @@
#include "chprintf.h"
#include "irq_controls.hpp"
#include "string_format.hpp"
#include "usb_serial_io.h"
#include "usb_serial_device_to_host.h"
using namespace portapack;
@@ -2118,7 +2118,15 @@ bool NumberField::on_key(const KeyEvent key) {
}
bool NumberField::on_encoder(const EncoderEvent delta) {
int32_t old_value = value();
set_value(value() + (delta * step));
if (on_wrap) {
if ((delta > 0) && (value() < old_value))
on_wrap(1);
else if ((delta < 0) && (value() > old_value))
on_wrap(-1);
}
return true;
}
+1
View File
@@ -783,6 +783,7 @@ class NumberField : public Widget {
public:
std::function<void(NumberField&)> on_select{};
std::function<void(int32_t)> on_change{};
std::function<void(int32_t)> on_wrap{};
using range_t = std::pair<int32_t, int32_t>;
+1 -13
View File
@@ -53,7 +53,6 @@ class UnTar {
}
static bool create_dir(char* pathname) {
char* p;
std::filesystem::filesystem_error r;
if (!isValidName(pathname)) return false;
@@ -65,18 +64,7 @@ class UnTar {
std::string dirnameStr = u'/' + pathname;
std::filesystem::path dirname = dirnameStr;
r = make_new_directory(dirname);
if (!r.ok()) {
/* On failure, try creating parent directory. */
p = strrchr(pathname, '/');
if (p != NULL) {
*p = '\0';
create_dir(pathname);
*p = '/';
r = make_new_directory(dirname);
}
}
r = ensure_directory(dirname);
return (r.ok());
}