Compare commits
5 Commits
7cd8d821f6
...
cd4b67e68f
| Author | SHA1 | Date | |
|---|---|---|---|
| cd4b67e68f | |||
| cfad427ec1 | |||
| 353babc103 | |||
| cd3a18e216 | |||
| 16fedf6792 |
@ -4,3 +4,5 @@ idf_component_register(
|
|||||||
SRCS ${SOURCE_FILES}
|
SRCS ${SOURCE_FILES}
|
||||||
REQUIRES TactilitySDK
|
REQUIRES TactilitySDK
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=uninitialized -Wno-error=maybe-uninitialized)
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
#include "RadioSet.h"
|
#include "RadioSet.h"
|
||||||
|
#include "Str.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <sstream>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <tt_lvgl_toolbar.h>
|
#include <tt_lvgl_toolbar.h>
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
#include "tt_app_alertdialog.h"
|
#include "tt_app_alertdialog.h"
|
||||||
|
|
||||||
constexpr const char* TAG = "RadioSet";
|
constexpr const char* TAG = "RadioSet";
|
||||||
@ -15,59 +15,89 @@ void crash(const char* const message) {
|
|||||||
tt_app_alertdialog_start("RadioSet has crashed!", message, nullptr, 0);
|
tt_app_alertdialog_start("RadioSet has crashed!", message, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug function which colors all children randomly
|
||||||
|
// TODO: Remove before flight
|
||||||
|
void clownvomit(lv_obj_t *obj) {
|
||||||
|
static auto rng = []() {
|
||||||
|
static int color = 0xC0FE;
|
||||||
|
const int a = 4711;
|
||||||
|
const int m = 0x10001;
|
||||||
|
color = (a * color) % m;
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
const int darken = 0x0E0E0E;
|
||||||
|
const int lighten = 0xFEFEFE;
|
||||||
|
lv_obj_set_style_bg_color(obj, lv_color_hex(rng() & darken), 0);
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
for(i = 0; i < lv_obj_get_child_count(obj); i++) {
|
||||||
|
lv_obj_t * child = lv_obj_get_child(obj, i);
|
||||||
|
lv_obj_set_style_bg_color(child, lv_color_hex(rng() & darken), 0);
|
||||||
|
lv_obj_set_style_border_color(child, lv_color_hex(rng() | lighten), 0);
|
||||||
|
lv_obj_set_style_border_width(child, 1, 0);
|
||||||
|
clownvomit(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
void scrollbar_highlight(lv_event_t * e) {
|
||||||
|
lv_obj_t * obj = lv_event_get_target(e);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
char *const toString(Modulation m) {
|
||||||
|
switch (m) {
|
||||||
|
case MODULATION_NONE:
|
||||||
|
return "None";
|
||||||
|
case MODULATION_LORA:
|
||||||
|
return "LoRa";
|
||||||
|
case MODULATION_FSK:
|
||||||
|
return "FSK";
|
||||||
|
case MODULATION_LRFHSS:
|
||||||
|
return "LR-FHSS";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crash("Unknown modulation passed.");
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *const toString(RadioParameter p) {
|
||||||
|
switch (p) {
|
||||||
|
case RADIO_POWER:
|
||||||
|
return "Power";
|
||||||
|
case RADIO_FREQUENCY:
|
||||||
|
return "Center Frequency";
|
||||||
|
case RADIO_BANDWIDTH:
|
||||||
|
return "Bandwidth";
|
||||||
|
case RADIO_SPREADFACTOR:
|
||||||
|
return "Spread Factor";
|
||||||
|
case RADIO_CODINGRATE:
|
||||||
|
return "Coding Rate";
|
||||||
|
case RADIO_SYNCWORD:
|
||||||
|
return "Sync Word";
|
||||||
|
case RADIO_PREAMBLES:
|
||||||
|
return "Preamble Length";
|
||||||
|
case RADIO_FREQDIV:
|
||||||
|
return "Frequency Deviation";
|
||||||
|
case RADIO_DATARATE:
|
||||||
|
return "Data Rate";
|
||||||
|
case RADIO_ADDRWIDTH:
|
||||||
|
return "Address Width";
|
||||||
|
case RADIO_NARROWGRID:
|
||||||
|
return "Narrow Grid";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crash("Unknown parameter passed.");
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
class TermView {
|
class TermView {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SettingsView {
|
|
||||||
private:
|
|
||||||
static constexpr size_t MAX_RADIOS = 32;
|
|
||||||
std::vector<RadioHandle> radios;
|
|
||||||
|
|
||||||
lv_obj_t *deviceForm = nullptr;
|
static lv_obj_t* initGridDropdownInput(lv_obj_t *container, int row, const char* const label, const char* const items) {
|
||||||
lv_obj_t *radioDropdown = nullptr;
|
|
||||||
lv_obj_t *radioSwitch = nullptr;
|
|
||||||
|
|
||||||
lv_obj_t *propertiesForm = nullptr;
|
|
||||||
public:
|
|
||||||
void queryRadios() {
|
|
||||||
std::vector<DeviceId> devices(MAX_RADIOS);
|
|
||||||
uint16_t radioCount = 0;
|
|
||||||
if(!tt_hal_device_find(DEVICE_TYPE_RADIO, devices.data(), &radioCount, devices.capacity())) {
|
|
||||||
// TT_LOG_W(TAG, "No radios registered with the system?");
|
|
||||||
} else {
|
|
||||||
devices.resize(radioCount);
|
|
||||||
radios.reserve(devices.size());
|
|
||||||
for (auto devId : devices) {
|
|
||||||
auto radio = tt_hal_radio_alloc(devId);
|
|
||||||
if (!radio) {
|
|
||||||
// TT_LOG_E(TAG, "Error allocating radio handle for id=%d", devId);
|
|
||||||
} else {
|
|
||||||
radios.push_back(radio);
|
|
||||||
// TT_LOG_I(TAG, "Discovered radio \"%s\"", tt_hal_radio_get_name(radio));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getRadioNames(std::vector<std::string> &names) {
|
|
||||||
int count = 1;
|
|
||||||
names.clear();
|
|
||||||
for (auto radio : radios) {
|
|
||||||
std::string name(tt_hal_radio_get_name(radio));
|
|
||||||
if (name == "") {
|
|
||||||
std::ostringstream oss("Unknown Radio ");
|
|
||||||
oss << count;
|
|
||||||
name = oss.str();
|
|
||||||
}
|
|
||||||
names.push_back(name);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !names.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_obj_t* initGridDropdownInput(lv_obj_t *container, int row, const char* const label, const char* const items) {
|
|
||||||
lv_obj_t* label_obj = lv_label_create(container);
|
lv_obj_t* label_obj = lv_label_create(container);
|
||||||
lv_label_set_text(label_obj, label);
|
lv_label_set_text(label_obj, label);
|
||||||
lv_obj_set_grid_cell(label_obj,
|
lv_obj_set_grid_cell(label_obj,
|
||||||
@ -85,53 +115,505 @@ public:
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static lv_obj_t* initGridTextInput(lv_obj_t *container, int row, const char* const label, const char* const defval, const char* const unit) {
|
||||||
|
const int height = LV_SIZE_CONTENT;
|
||||||
|
lv_obj_t* label_obj = lv_label_create(container);
|
||||||
|
lv_label_set_text(label_obj, label);
|
||||||
|
lv_obj_set_grid_cell(label_obj,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label_obj, lv_pct(100), height);
|
||||||
|
|
||||||
|
lv_obj_t* input = lv_textarea_create(container);
|
||||||
|
lv_obj_set_grid_cell(input,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(input, lv_pct(100), height);
|
||||||
|
lv_textarea_set_text(input, defval);
|
||||||
|
lv_textarea_set_one_line(input, true);
|
||||||
|
|
||||||
|
lv_obj_t* unit_obj = lv_label_create(container);
|
||||||
|
lv_label_set_text(unit_obj, unit);
|
||||||
|
lv_obj_set_grid_cell(unit_obj,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(unit_obj, lv_pct(100), height);
|
||||||
|
lv_obj_set_style_text_align(unit_obj , LV_TEXT_ALIGN_CENTER, 0);
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParameterInput {
|
||||||
|
const RadioHandle handle;
|
||||||
|
const RadioParameter param;
|
||||||
|
|
||||||
|
ParameterInput(RadioHandle handle, const RadioParameter param)
|
||||||
|
: handle(handle)
|
||||||
|
, param(param) {}
|
||||||
|
virtual void updatePreview() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NumericParameterInput : public ParameterInput {
|
||||||
|
|
||||||
|
lv_obj_t* label = nullptr;
|
||||||
|
lv_obj_t* input = nullptr;
|
||||||
|
lv_obj_t* unitlabel = nullptr;
|
||||||
|
char* fmt;
|
||||||
|
|
||||||
|
NumericParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, char* fmt = "%f", char* unit_override = nullptr)
|
||||||
|
: ParameterInput(handle, param)
|
||||||
|
, fmt(fmt) {
|
||||||
|
initUi(container, row, unit_override);
|
||||||
|
loadFromRadio();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadFromRadio() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUi(lv_obj_t* container, int row, char* unit_override) {
|
||||||
|
const int height = LV_SIZE_CONTENT;
|
||||||
|
label = lv_label_create(container);
|
||||||
|
lv_label_set_text(label, toString(param));
|
||||||
|
lv_obj_set_grid_cell(label,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label, lv_pct(100), height);
|
||||||
|
|
||||||
|
input = lv_textarea_create(container);
|
||||||
|
lv_obj_set_grid_cell(input,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(input, lv_pct(100), height);
|
||||||
|
//TODO: LOAD VALUE
|
||||||
|
//lv_textarea_set_text(input, defval);
|
||||||
|
lv_textarea_set_one_line(input, true);
|
||||||
|
|
||||||
|
unitlabel = lv_label_create(container);
|
||||||
|
lv_obj_set_grid_cell(unitlabel,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(unitlabel, lv_pct(100), height);
|
||||||
|
lv_obj_set_style_text_align(unitlabel , LV_TEXT_ALIGN_CENTER, 0);
|
||||||
|
|
||||||
|
if (unit_override) {
|
||||||
|
lv_label_set_text(unitlabel, unit_override);
|
||||||
|
} else {
|
||||||
|
char unit[64] = {0};
|
||||||
|
tt_hal_radio_get_parameter_unit_str(handle, param, unit, sizeof(unit));
|
||||||
|
lv_label_set_text(unitlabel, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePreview() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SliderParameterInput : public ParameterInput {
|
||||||
|
char* fmt = nullptr;
|
||||||
|
lv_obj_t* label = nullptr;
|
||||||
|
lv_obj_t* slider = nullptr;
|
||||||
|
lv_obj_t* preview = nullptr;
|
||||||
|
|
||||||
|
SliderParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, int min, int max, char* fmt = "%i")
|
||||||
|
: ParameterInput(handle, param)
|
||||||
|
, fmt(fmt) {
|
||||||
|
initUi(container, row, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUi(lv_obj_t* container, int row, int min, int max) {
|
||||||
|
label = lv_label_create(container);
|
||||||
|
lv_label_set_text(label, toString(param));
|
||||||
|
lv_obj_set_grid_cell(label,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
slider = lv_slider_create(container);
|
||||||
|
lv_obj_set_grid_cell(slider,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(slider, lv_pct(100), 10);
|
||||||
|
lv_slider_set_range(slider, min, max);
|
||||||
|
|
||||||
|
preview = lv_label_create(container);
|
||||||
|
lv_obj_set_grid_cell(preview,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(preview, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);
|
||||||
|
updatePreview();
|
||||||
|
lv_obj_add_event_cb(slider, [](lv_event_t * e) {
|
||||||
|
lv_obj_t* slider = lv_event_get_target_obj(e);
|
||||||
|
SliderParameterInput* self = (SliderParameterInput*)lv_event_get_user_data(e);
|
||||||
|
self->updatePreview();
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePreview() override {
|
||||||
|
char buf[64] = {0};
|
||||||
|
lv_snprintf(buf, sizeof(buf), fmt, lv_slider_get_value(slider));
|
||||||
|
lv_label_set_text(preview, buf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SliderSelectParameterInput : public ParameterInput {
|
||||||
|
static constexpr float SELECT_END = -1;
|
||||||
|
const float* selections;
|
||||||
|
const size_t selectionsSize;
|
||||||
|
|
||||||
|
char unit[64] = {0};
|
||||||
|
char* fmt;
|
||||||
|
lv_obj_t* label = nullptr;
|
||||||
|
lv_obj_t* slider = nullptr;
|
||||||
|
lv_obj_t* preview = nullptr;
|
||||||
|
|
||||||
|
static constexpr size_t get_selection_num(const float selections[]) {
|
||||||
|
constexpr size_t MAX_SELECTIONS = 32;
|
||||||
|
|
||||||
|
if (selections) {
|
||||||
|
for (size_t i = 0; i < MAX_SELECTIONS; ++i) {
|
||||||
|
if (selections[i] == SELECT_END) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SliderSelectParameterInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row, const float selections[] = nullptr, char* fmt = "%f")
|
||||||
|
: ParameterInput(handle, param)
|
||||||
|
, selections(selections)
|
||||||
|
, selectionsSize(get_selection_num(selections))
|
||||||
|
, fmt(fmt) {
|
||||||
|
initUi(container, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initUi(lv_obj_t* container, int row) {
|
||||||
|
label = lv_label_create(container);
|
||||||
|
lv_label_set_text(label, toString(param));
|
||||||
|
lv_obj_set_grid_cell(label,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 0, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(label, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
|
slider = lv_slider_create(container);
|
||||||
|
lv_obj_set_grid_cell(slider,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 1, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(slider, lv_pct(100), 10);
|
||||||
|
lv_slider_set_range(slider, 0, selectionsSize - 1);
|
||||||
|
|
||||||
|
preview = lv_label_create(container);
|
||||||
|
lv_obj_set_grid_cell(preview,
|
||||||
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
|
LV_GRID_ALIGN_CENTER, row, 1);
|
||||||
|
lv_obj_set_size(preview, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);
|
||||||
|
|
||||||
|
tt_hal_radio_get_parameter_unit_str(handle, param, unit, sizeof(unit));
|
||||||
|
updatePreview();
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(slider, [](lv_event_t * e) {
|
||||||
|
lv_obj_t* slider = lv_event_get_target_obj(e);
|
||||||
|
SliderSelectParameterInput* self = (SliderSelectParameterInput*)lv_event_get_user_data(e);
|
||||||
|
self->updatePreview();
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void updatePreview() override {
|
||||||
|
Str text;
|
||||||
|
text.appendf(fmt, selections[lv_slider_get_value(slider)]);
|
||||||
|
text.append(unit);
|
||||||
|
lv_label_set_text(preview, text.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static ParameterInput* makeLoraInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row) {
|
||||||
|
static constexpr float bw_values[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125.0, 250.0, 500.0, SliderSelectParameterInput::SELECT_END};
|
||||||
|
switch (param) {
|
||||||
|
case RADIO_POWER: //no break
|
||||||
|
case RADIO_FREQUENCY:
|
||||||
|
return new NumericParameterInput(handle, param, container, row);
|
||||||
|
case RADIO_BANDWIDTH:
|
||||||
|
return new SliderSelectParameterInput(handle, param, container, row, bw_values, "%.1f");
|
||||||
|
case RADIO_SPREADFACTOR:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 7, 12);
|
||||||
|
case RADIO_CODINGRATE:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 5, 8);
|
||||||
|
case RADIO_SYNCWORD:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 0, 255, "%02X");
|
||||||
|
case RADIO_PREAMBLES:
|
||||||
|
return new SliderParameterInput(handle, param, container, row, 0, 0xFFFF);
|
||||||
|
default:
|
||||||
|
return new NumericParameterInput(handle, param, container, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterInput* makeParameterInput(RadioHandle handle, const RadioParameter param, const Modulation modulation, lv_obj_t* container, int row) {
|
||||||
|
switch (modulation) {
|
||||||
|
case MODULATION_LORA:
|
||||||
|
return makeLoraInput(handle, param, container, row);
|
||||||
|
default:
|
||||||
|
return new NumericParameterInput(handle, param, container, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsView {
|
||||||
|
static constexpr size_t MAX_RADIOS = 32;
|
||||||
|
static constexpr Modulation FIRST_MODULATION = MODULATION_NONE;
|
||||||
|
static constexpr Modulation LAST_MODULATION = MODULATION_LRFHSS;
|
||||||
|
static constexpr size_t MAX_MODEMS = LAST_MODULATION + 1;
|
||||||
|
static constexpr size_t MAX_PARAMS = RADIO_NARROWGRID + 1;
|
||||||
|
|
||||||
|
RadioHandle radios[MAX_RADIOS] = {0};
|
||||||
|
size_t radioCount = 0;
|
||||||
|
|
||||||
|
RadioHandle radioSelected = nullptr;
|
||||||
|
Modulation modemsAvailable[MAX_MODEMS] = {};
|
||||||
|
size_t modemsAvailableCount = 0;
|
||||||
|
RadioParameter paramsAvailable[MAX_PARAMS] = {};
|
||||||
|
ParameterInput* paramInputs[MAX_PARAMS] = {0};
|
||||||
|
size_t paramsAvailableCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
lv_obj_t *mainPanel = nullptr;
|
||||||
|
lv_obj_t *deviceForm = nullptr;
|
||||||
|
lv_obj_t *radioDropdown = nullptr;
|
||||||
|
lv_obj_t *radioSwitch = nullptr;
|
||||||
|
lv_obj_t *modemDropdown = nullptr;
|
||||||
|
|
||||||
|
lv_obj_t *propertiesForm = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void queryRadios() {
|
||||||
|
DeviceId devices[MAX_RADIOS];
|
||||||
|
uint16_t device_count = 0;
|
||||||
|
if(!tt_hal_device_find(DEVICE_TYPE_RADIO, devices, &device_count, MAX_RADIOS)) {
|
||||||
|
// TT_LOG_W(TAG, "No radios registered with the system?");
|
||||||
|
} else {
|
||||||
|
size_t radios_allocated = 0;
|
||||||
|
for (size_t i = 0; (i < device_count) && (i < MAX_RADIOS); ++i) {
|
||||||
|
auto radio = tt_hal_radio_alloc(devices[i]);
|
||||||
|
if (radio) {
|
||||||
|
// TT_LOG_I(TAG, "Discovered radio \"%s\"", tt_hal_radio_get_name(radio));
|
||||||
|
radios[radios_allocated] = radio;
|
||||||
|
radios_allocated++;
|
||||||
|
} else {
|
||||||
|
// TT_LOG_E(TAG, "Error allocating radio handle for id=%d", devId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
radioCount = radios_allocated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getRadioNames(Str &names, const char* const separator) {
|
||||||
|
int count = 1;
|
||||||
|
names.clear();
|
||||||
|
//for (auto radio : radios) {
|
||||||
|
for (size_t i = 0; i < radioCount; ++i) {
|
||||||
|
Str name(tt_hal_radio_get_name(radios[i]));
|
||||||
|
auto last = (i == (radioCount - 1));
|
||||||
|
if (name == "") {
|
||||||
|
name.appendf("Unknown Radio %d", count);
|
||||||
|
}
|
||||||
|
names.append(name.c_str());
|
||||||
|
count++;
|
||||||
|
if (!last) {
|
||||||
|
names.append(separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getModemAvailableIndex(Modulation m) {
|
||||||
|
for (size_t i = 0; i < modemsAvailableCount; ++i) {
|
||||||
|
if (modemsAvailable[i] == m) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_t* initParameterFormGeneric(lv_obj_t *parent, const Modulation modem) {
|
||||||
|
lv_obj_t *container = propertiesForm;
|
||||||
|
if (container) {
|
||||||
|
lv_obj_clean(container);
|
||||||
|
lv_obj_del(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
paramsAvailableCount = 0;
|
||||||
|
container = lv_obj_create(parent);
|
||||||
|
lv_obj_set_style_pad_all(container, 0, 0);
|
||||||
|
lv_obj_set_layout(container, LV_LAYOUT_GRID);
|
||||||
|
lv_obj_align(container, LV_ALIGN_TOP_MID, 0, 0);
|
||||||
|
|
||||||
|
const int grid_row_size = 40;
|
||||||
|
const int grid_col_size = 140;
|
||||||
|
static constexpr size_t row_dsc_last = RADIO_NARROWGRID + 1;
|
||||||
|
static lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), grid_col_size, LV_GRID_TEMPLATE_LAST};
|
||||||
|
static lv_coord_t row_dsc[row_dsc_last] = {0};
|
||||||
|
for (size_t i = 0; i < row_dsc_last; ++i) {
|
||||||
|
row_dsc[i] = grid_row_size; //LV_GRID_FR(1);
|
||||||
|
}
|
||||||
|
row_dsc[row_dsc_last - 1] = LV_GRID_TEMPLATE_LAST;
|
||||||
|
lv_obj_set_grid_dsc_array(container, col_dsc, row_dsc);
|
||||||
|
|
||||||
|
char unit_buffer[32] = {0};
|
||||||
|
for (RadioParameter param = RADIO_POWER;
|
||||||
|
param <= RADIO_NARROWGRID;
|
||||||
|
param = static_cast<RadioParameter>((size_t)param + 1)) {
|
||||||
|
float value = 0.0;
|
||||||
|
Str value_buffer;
|
||||||
|
auto status = tt_hal_radio_get_parameter(radioSelected, param, &value);
|
||||||
|
if (status == RADIO_PARAM_SUCCESS) {
|
||||||
|
auto input = makeParameterInput(radioSelected, param, modem, container, paramsAvailableCount);
|
||||||
|
paramInputs[paramsAvailableCount] = input;
|
||||||
|
//lv_group_focus_obj(input);
|
||||||
|
paramsAvailable[paramsAvailableCount] = param;
|
||||||
|
paramsAvailableCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row_dsc[paramsAvailableCount] = LV_GRID_TEMPLATE_LAST;
|
||||||
|
lv_obj_set_grid_dsc_array(container, col_dsc, row_dsc);
|
||||||
|
lv_obj_set_size(container, lv_pct(100), lv_pct(100));
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectModulation(int modemIndex) {
|
||||||
|
lv_dropdown_set_selected(modemDropdown, modemIndex);
|
||||||
|
if (tt_hal_radio_set_modulation(radioSelected, modemsAvailable[modemIndex])) {
|
||||||
|
propertiesForm = initParameterFormGeneric(mainPanel, modemsAvailable[modemIndex]);
|
||||||
|
//clownvomit(propertiesForm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectRadio(int index) {
|
||||||
|
radioSelected = radios[index];
|
||||||
|
assert(radioSelected);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_MODEMS; ++i) {
|
||||||
|
modemsAvailable[i] = MODULATION_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Str modulation_list;
|
||||||
|
modemsAvailableCount = 1;
|
||||||
|
modemsAvailable[0] = MODULATION_NONE;
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
modulation_list.append("Disabled\n");
|
||||||
|
|
||||||
|
|
||||||
|
for (Modulation mod = FIRST_MODULATION;
|
||||||
|
mod <= LAST_MODULATION;
|
||||||
|
mod = static_cast<Modulation>((size_t)mod + 1)) {
|
||||||
|
bool canRx = tt_hal_radio_can_receive(radioSelected, mod);
|
||||||
|
bool canTx = tt_hal_radio_can_transmit(radioSelected, mod);
|
||||||
|
bool place_sep = (canRx || canTx) && (mod != LAST_MODULATION);
|
||||||
|
|
||||||
|
if (!canRx && !canTx) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
modemsAvailable[modemsAvailableCount] = mod;
|
||||||
|
modemsAvailableCount++;
|
||||||
|
|
||||||
|
if (canRx) {
|
||||||
|
modulation_list.append(LV_SYMBOL_DOWNLOAD);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
} else {
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canTx) {
|
||||||
|
modulation_list.append(LV_SYMBOL_UPLOAD);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
} else {
|
||||||
|
modulation_list.append(LV_SYMBOL_MINUS);
|
||||||
|
modulation_list.append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
modulation_list.append(toString(mod));
|
||||||
|
if (place_sep) {
|
||||||
|
modulation_list.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_dropdown_set_options(modemDropdown, modulation_list.c_str());
|
||||||
|
auto modemIndexConfigured = getModemAvailableIndex(tt_hal_radio_get_modulation(radioSelected));
|
||||||
|
if (modemIndexConfigured > -1) {
|
||||||
|
lv_dropdown_set_selected(modemDropdown, modemIndexConfigured);
|
||||||
|
selectModulation(modemIndexConfigured);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lv_obj_t *initDeviceForm(lv_obj_t *parent) {
|
lv_obj_t *initDeviceForm(lv_obj_t *parent) {
|
||||||
lv_obj_t *container = lv_obj_create(parent);
|
lv_obj_t *container = lv_obj_create(parent);
|
||||||
lv_obj_set_size(container, lv_pct(100), lv_pct(100));
|
lv_obj_set_size(container, lv_pct(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_all(container, 0, 0);
|
lv_obj_set_style_pad_all(container, 0, 0);
|
||||||
|
|
||||||
const int grid_row_size = 40;
|
const int grid_row_size = 40;
|
||||||
const int grid_col_size = 45;
|
const int grid_col_size = 45;
|
||||||
static lv_coord_t lora_col_dsc[] = {LV_GRID_FR(3), LV_GRID_FR(2), grid_col_size, LV_GRID_TEMPLATE_LAST};
|
static lv_coord_t lora_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), grid_col_size, LV_GRID_TEMPLATE_LAST};
|
||||||
static lv_coord_t lora_row_dsc[] = {
|
static lv_coord_t lora_row_dsc[] = {
|
||||||
grid_row_size,
|
grid_row_size,
|
||||||
grid_row_size,
|
grid_row_size,
|
||||||
LV_GRID_TEMPLATE_LAST};
|
LV_GRID_TEMPLATE_LAST};
|
||||||
|
lv_obj_set_layout(container, LV_LAYOUT_GRID);
|
||||||
lv_obj_set_grid_dsc_array(container, lora_col_dsc, lora_row_dsc);
|
lv_obj_set_grid_dsc_array(container, lora_col_dsc, lora_row_dsc);
|
||||||
|
|
||||||
std::ostringstream item_oss("");
|
Str radio_names;
|
||||||
std::vector<std::string> radio_names;
|
getRadioNames(radio_names, "\n");
|
||||||
if (getRadioNames(radio_names)) {
|
radioDropdown = initGridDropdownInput(container, 0, "Device", radio_names.c_str());
|
||||||
for (size_t i = 0; i < radio_names.size(); ++i) {
|
modemDropdown = initGridDropdownInput(container, 1, "Modulation", "none available");
|
||||||
auto name = radio_names[i];
|
|
||||||
auto last = (i != (radio_names.size() - 1));
|
|
||||||
item_oss << name;
|
|
||||||
if (!last) {
|
|
||||||
item_oss << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
radioDropdown = initGridDropdownInput(container, 0, "Device", item_oss.str().c_str());
|
|
||||||
radioSwitch = lv_switch_create(container);
|
radioSwitch = lv_switch_create(container);
|
||||||
lv_obj_set_grid_cell(radioSwitch,
|
lv_obj_set_grid_cell(radioSwitch,
|
||||||
LV_GRID_ALIGN_STRETCH, 2, 1,
|
LV_GRID_ALIGN_STRETCH, 2, 1,
|
||||||
LV_GRID_ALIGN_CENTER, 0, 1);
|
LV_GRID_ALIGN_CENTER, 0, 1);
|
||||||
lv_obj_set_size(radioSwitch, lv_pct(100), 20);
|
lv_obj_set_size(radioSwitch, lv_pct(100), 20);
|
||||||
|
|
||||||
|
|
||||||
|
lv_obj_add_event_cb(modemDropdown, [](lv_event_t * e) {
|
||||||
|
SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
|
||||||
|
lv_obj_t* input = lv_event_get_target_obj(e);
|
||||||
|
self->selectModulation(lv_dropdown_get_selected(input));
|
||||||
|
}, LV_EVENT_VALUE_CHANGED, this);
|
||||||
|
|
||||||
|
selectRadio(0);
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initUi(lv_obj_t *parent) {
|
void initUi(lv_obj_t *parent) {
|
||||||
lv_obj_t *container = lv_obj_create(parent);
|
mainPanel = lv_obj_create(parent);
|
||||||
lv_obj_set_size(container, lv_pct(100), lv_pct(80));
|
lv_obj_set_size(mainPanel, lv_pct(100), lv_pct(80));
|
||||||
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(mainPanel, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_align(container, LV_ALIGN_TOP_MID, 0, 0);
|
lv_obj_align(mainPanel, LV_ALIGN_TOP_MID, 0, 0);
|
||||||
lv_obj_set_style_border_width(container, 0, 0);
|
lv_obj_set_style_border_width(mainPanel, 0, 0);
|
||||||
lv_obj_set_style_pad_all(container, 0, 0);
|
lv_obj_set_style_pad_all(mainPanel, 0, 0);
|
||||||
// Only needed if container needs to be scrollable through encoder long long press
|
// Only needed if container needs to be scrollable through encoder long long press
|
||||||
//lv_obj_add_flag(container, (lv_obj_flag_t)(LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLL_ON_FOCUS));
|
lv_obj_add_flag(mainPanel, (lv_obj_flag_t)(LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLL_ON_FOCUS));
|
||||||
deviceForm = initDeviceForm(container);
|
auto* group = lv_group_get_default();
|
||||||
|
lv_group_add_obj(group, mainPanel);
|
||||||
|
//lv_obj_add_event_cb(btn, scrollbar_highlight, LV_EVENT_FOCUS, NULL);
|
||||||
|
//lv_obj_add_event_cb(btn, scrollbar_restore, LV_EVENT_DEFOCUSED, NULL);
|
||||||
|
|
||||||
|
// Create once (e.g. during init)
|
||||||
|
static lv_style_t style_scroll_focus;
|
||||||
|
lv_style_init(&style_scroll_focus);
|
||||||
|
lv_style_set_bg_color(&style_scroll_focus, lv_color_make(0x40,0xA0,0xFF));
|
||||||
|
lv_style_set_bg_opa(&style_scroll_focus, LV_OPA_COVER);
|
||||||
|
lv_style_set_border_width(&style_scroll_focus, 1);
|
||||||
|
lv_style_set_border_color(&style_scroll_focus, lv_color_black());
|
||||||
|
|
||||||
|
// Apply style targeted to the scrollbar part when the object is FOCUSED
|
||||||
|
// LV_PART_SCROLLBAR | LV_STATE_FOCUSED will ensure the style is used only while focused.
|
||||||
|
lv_obj_add_style(mainPanel, &style_scroll_focus, LV_PART_SCROLLBAR | LV_STATE_FOCUSED);
|
||||||
|
|
||||||
|
deviceForm = initDeviceForm(mainPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit SettingsView(lv_obj_t *parent) {
|
explicit SettingsView(lv_obj_t *parent) {
|
||||||
@ -157,6 +639,7 @@ void RadioSet::onShow(AppHandle appHandle, lv_obj_t* parent) {
|
|||||||
lv_obj_set_style_border_color(uiDropDownMenu, lv_color_hex(0xFAFAFA), LV_PART_MAIN);
|
lv_obj_set_style_border_color(uiDropDownMenu, lv_color_hex(0xFAFAFA), LV_PART_MAIN);
|
||||||
lv_obj_set_style_border_width(uiDropDownMenu, 1, LV_PART_MAIN);
|
lv_obj_set_style_border_width(uiDropDownMenu, 1, LV_PART_MAIN);
|
||||||
lv_obj_align(uiDropDownMenu, LV_ALIGN_RIGHT_MID, 0, 0);
|
lv_obj_align(uiDropDownMenu, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||||
|
lv_obj_set_width(uiDropDownMenu, 120);
|
||||||
|
|
||||||
lv_obj_t* wrapper = lv_obj_create(parent);
|
lv_obj_t* wrapper = lv_obj_create(parent);
|
||||||
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
@ -169,7 +652,17 @@ void RadioSet::onShow(AppHandle appHandle, lv_obj_t* parent) {
|
|||||||
lv_obj_set_style_border_width(wrapper, 0, 0);
|
lv_obj_set_style_border_width(wrapper, 0, 0);
|
||||||
lv_obj_remove_flag(wrapper, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_remove_flag(wrapper, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
settingsView = std::make_shared<SettingsView>(wrapper);
|
settingsView = new SettingsView(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RadioSet::~RadioSet() {
|
||||||
|
if (termView) {
|
||||||
|
delete termView;
|
||||||
|
}
|
||||||
|
if (settingsView) {
|
||||||
|
delete settingsView;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ????
|
// ????
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "tt_app.h"
|
#include "tt_app.h"
|
||||||
#include "tt_hal_radio.h"
|
#include "tt_hal_radio.h"
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@ -15,10 +13,12 @@ class RadioSet {
|
|||||||
lv_obj_t* progressBar = nullptr;
|
lv_obj_t* progressBar = nullptr;
|
||||||
lv_obj_t* progressText = nullptr;
|
lv_obj_t* progressText = nullptr;
|
||||||
|
|
||||||
std::shared_ptr<TermView> termView = nullptr;
|
TermView* termView = nullptr;
|
||||||
std::shared_ptr<SettingsView> settingsView = nullptr;
|
SettingsView* settingsView = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
~RadioSet();
|
||||||
|
|
||||||
void onShow(AppHandle context, lv_obj_t* parent);
|
void onShow(AppHandle context, lv_obj_t* parent);
|
||||||
};
|
};
|
||||||
|
|||||||
2
ExternalApps/RadioSet/main/Source/Str.cpp
Normal file
2
ExternalApps/RadioSet/main/Source/Str.cpp
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#define STR_IMPLEMENTATION
|
||||||
|
#include "Str.h"
|
||||||
618
ExternalApps/RadioSet/main/Source/Str.h
Normal file
618
ExternalApps/RadioSet/main/Source/Str.h
Normal file
@ -0,0 +1,618 @@
|
|||||||
|
// Str v0.33
|
||||||
|
// Simple C++ string type with an optional local buffer, by Omar Cornut
|
||||||
|
// https://github.com/ocornut/str
|
||||||
|
|
||||||
|
// LICENSE
|
||||||
|
// This software is in the public domain. Where that dedication is not
|
||||||
|
// recognized, you are granted a perpetual, irrevocable license to copy,
|
||||||
|
// distribute, and modify this file as you see fit.
|
||||||
|
|
||||||
|
// USAGE
|
||||||
|
// Include this file in whatever places need to refer to it.
|
||||||
|
// In ONE .cpp file, write '#define STR_IMPLEMENTATION' before the #include of this file.
|
||||||
|
// This expands out the actual implementation into that C/C++ file.
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
- This isn't a fully featured string class.
|
||||||
|
- It is a simple, bearable replacement to std::string that isn't heap abusive nor bloated (can actually be debugged by humans).
|
||||||
|
- String are mutable. We don't maintain size so length() is not-constant time.
|
||||||
|
- Maximum string size currently limited to 2 MB (we allocate 21 bits to hold capacity).
|
||||||
|
- Local buffer size is currently limited to 1023 bytes (we allocate 10 bits to hold local buffer size).
|
||||||
|
- In "non-owned" mode for literals/reference we don't do any tracking/counting of references.
|
||||||
|
- Overhead is 8-bytes in 32-bits, 16-bytes in 64-bits (12 + alignment).
|
||||||
|
- This code hasn't been tested very much. it is probably incomplete or broken. Made it for my own use.
|
||||||
|
|
||||||
|
The idea is that you can provide an arbitrary sized local buffer if you expect string to fit
|
||||||
|
most of the time, and then you avoid using costly heap.
|
||||||
|
|
||||||
|
No local buffer, always use heap, sizeof()==8~16 (depends if your pointers are 32-bits or 64-bits)
|
||||||
|
|
||||||
|
Str s = "hey";
|
||||||
|
|
||||||
|
With a local buffer of 16 bytes, sizeof() == 8~16 + 16 bytes.
|
||||||
|
|
||||||
|
Str16 s = "filename.h"; // copy into local buffer
|
||||||
|
Str16 s = "long_filename_not_very_long_but_longer_than_expected.h"; // use heap
|
||||||
|
|
||||||
|
With a local buffer of 256 bytes, sizeof() == 8~16 + 256 bytes.
|
||||||
|
|
||||||
|
Str256 s = "long_filename_not_very_long_but_longer_than_expected.h"; // copy into local buffer
|
||||||
|
|
||||||
|
Common sizes are defined at the bottom of Str.h, you may define your own.
|
||||||
|
|
||||||
|
Functions:
|
||||||
|
|
||||||
|
Str256 s;
|
||||||
|
s.set("hello sailor"); // set (copy)
|
||||||
|
s.setf("%s/%s.tmp", folder, filename); // set (w/format)
|
||||||
|
s.append("hello"); // append. cost a length() calculation!
|
||||||
|
s.appendf("hello %d", 42); // append (w/format). cost a length() calculation!
|
||||||
|
s.set_ref("Hey!"); // set (literal/reference, just copy pointer, no tracking)
|
||||||
|
|
||||||
|
Constructor helper for format string: add a trailing 'f' to the type. Underlying type is the same.
|
||||||
|
|
||||||
|
Str256f filename("%s/%s.tmp", folder, filename); // construct (w/format)
|
||||||
|
fopen(Str256f("%s/%s.tmp, folder, filename).c_str(), "rb"); // construct (w/format), use as function param, destruct
|
||||||
|
|
||||||
|
Constructor helper for reference/literal:
|
||||||
|
|
||||||
|
StrRef ref("literal"); // copy pointer, no allocation, no string copy
|
||||||
|
StrRef ref2(GetDebugName()); // copy pointer. no tracking of anything whatsoever, know what you are doing!
|
||||||
|
|
||||||
|
All StrXXX types derives from Str and instance hold the local buffer capacity. So you can pass e.g. Str256* to a function taking base type Str* and it will be functional.
|
||||||
|
|
||||||
|
void MyFunc(Str& s) { s = "Hello"; } // will use local buffer if available in Str instance
|
||||||
|
|
||||||
|
(Using a template e.g. Str<N> we could remove the LocalBufSize storage but it would make passing typed Str<> to functions tricky.
|
||||||
|
Instead we don't use template so you can pass them around as the base type Str*. Also, templates are ugly.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
CHANGELOG
|
||||||
|
0.33 - fixed capacity() return value to match standard. e.g. a Str256's capacity() now returns 255, not 256.
|
||||||
|
0.32 - added owned() accessor.
|
||||||
|
0.31 - fixed various warnings.
|
||||||
|
0.30 - turned into a single header file, removed Str.cpp.
|
||||||
|
0.29 - fixed bug when calling reserve on non-owned strings (ie. when using StrRef or set_ref), and fixed <string> include.
|
||||||
|
0.28 - breaking change: replaced Str32 by Str30 to avoid collision with Str32 from MacTypes.h .
|
||||||
|
0.27 - added STR_API and basic .natvis file.
|
||||||
|
0.26 - fixed set(cont char* src, const char* src_end) writing null terminator to the wrong position.
|
||||||
|
0.25 - allow set(const char* NULL) or operator= NULL to clear the string. note that set() from range or other types are not allowed.
|
||||||
|
0.24 - allow set_ref(const char* NULL) to clear the string. include fixes for linux.
|
||||||
|
0.23 - added append(char). added append_from(int idx, XXX) functions. fixed some compilers warnings.
|
||||||
|
0.22 - documentation improvements, comments. fixes for some compilers.
|
||||||
|
0.21 - added StrXXXf() constructor to construct directly from a format string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
- Since we lose 4-bytes of padding on 64-bits architecture, perhaps just spread the header to 8-bytes and lift size limits?
|
||||||
|
- More functions/helpers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STR_INCLUDED
|
||||||
|
#define STR_INCLUDED
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// CONFIGURATION
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef STR_MEMALLOC
|
||||||
|
#define STR_MEMALLOC malloc
|
||||||
|
#include <cstdio>
|
||||||
|
#endif
|
||||||
|
#ifndef STR_MEMFREE
|
||||||
|
#define STR_MEMFREE free
|
||||||
|
#include <cstdlib>
|
||||||
|
#endif
|
||||||
|
#ifndef STR_ASSERT
|
||||||
|
#define STR_ASSERT assert
|
||||||
|
#include <cassert>
|
||||||
|
#endif
|
||||||
|
#ifndef STR_API
|
||||||
|
#define STR_API
|
||||||
|
#endif
|
||||||
|
#include <cstdarg> // for va_list
|
||||||
|
#include <cstring> // for strlen, strcmp, memcpy, etc.
|
||||||
|
|
||||||
|
// Configuration: #define STR_DEFINE_STR32 1 to keep defining Str32/Str32f, but be warned: on macOS/iOS, MacTypes.h also defines a type named Str32.
|
||||||
|
#ifndef STR_DEFINE_STR32
|
||||||
|
#define STR_DEFINE_STR32 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// HEADERS
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// This is the base class that you can pass around
|
||||||
|
// Footprint is 8-bytes (32-bits arch) or 16-bytes (64-bits arch)
|
||||||
|
class STR_API Str
|
||||||
|
{
|
||||||
|
char* Data; // Point to LocalBuf() or heap allocated
|
||||||
|
int Capacity : 21; // Max 2 MB. Exclude zero terminator.
|
||||||
|
int LocalBufSize : 10; // Max 1023 bytes
|
||||||
|
unsigned int Owned : 1; // Set when we have ownership of the pointed data (most common, unless using set_ref() method or StrRef constructor)
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline char* c_str() { return Data; }
|
||||||
|
inline const char* c_str() const { return Data; }
|
||||||
|
inline bool empty() const { return Data[0] == 0; }
|
||||||
|
inline int length() const { return (int)strlen(Data); } // by design, allow user to write into the buffer at any time
|
||||||
|
inline int capacity() const { return Capacity; }
|
||||||
|
inline bool owned() const { return Owned ? true : false; }
|
||||||
|
|
||||||
|
inline void set_ref(const char* src);
|
||||||
|
int setf(const char* fmt, ...);
|
||||||
|
int setfv(const char* fmt, va_list args);
|
||||||
|
int setf_nogrow(const char* fmt, ...);
|
||||||
|
int setfv_nogrow(const char* fmt, va_list args);
|
||||||
|
int append(char c);
|
||||||
|
int append(const char* s, const char* s_end = NULL);
|
||||||
|
int appendf(const char* fmt, ...);
|
||||||
|
int appendfv(const char* fmt, va_list args);
|
||||||
|
int append_from(int idx, char c);
|
||||||
|
int append_from(int idx, const char* s, const char* s_end = NULL); // If you know the string length or want to append from a certain point
|
||||||
|
int appendf_from(int idx, const char* fmt, ...);
|
||||||
|
int appendfv_from(int idx, const char* fmt, va_list args);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void reserve(int cap);
|
||||||
|
void reserve_discard(int cap);
|
||||||
|
void shrink_to_fit();
|
||||||
|
|
||||||
|
inline char& operator[](size_t i) { return Data[i]; }
|
||||||
|
inline char operator[](size_t i) const { return Data[i]; }
|
||||||
|
//explicit operator const char*() const{ return Data; }
|
||||||
|
|
||||||
|
inline Str();
|
||||||
|
inline Str(const char* rhs);
|
||||||
|
inline void set(const char* src);
|
||||||
|
inline void set(const char* src, const char* src_end);
|
||||||
|
inline Str& operator=(const char* rhs) { set(rhs); return *this; }
|
||||||
|
inline bool operator==(const char* rhs) const { return strcmp(c_str(), rhs) == 0; }
|
||||||
|
|
||||||
|
inline Str(const Str& rhs);
|
||||||
|
inline void set(const Str& src);
|
||||||
|
inline void set(int count, char character);
|
||||||
|
inline Str& operator=(const Str& rhs) { set(rhs); return *this; }
|
||||||
|
inline bool operator==(const Str& rhs) const { return strcmp(c_str(), rhs.c_str()) == 0; }
|
||||||
|
|
||||||
|
inline Str(int amount, char character);
|
||||||
|
|
||||||
|
// Destructor for all variants
|
||||||
|
inline ~Str()
|
||||||
|
{
|
||||||
|
if (Owned && !is_using_local_buf())
|
||||||
|
STR_MEMFREE(Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* EmptyBuffer;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline char* local_buf() { return (char*)this + sizeof(Str); }
|
||||||
|
inline const char* local_buf() const { return (char*)this + sizeof(Str); }
|
||||||
|
inline bool is_using_local_buf() const { return Data == local_buf() && LocalBufSize != 0; }
|
||||||
|
|
||||||
|
// Constructor for StrXXX variants with local buffer
|
||||||
|
Str(unsigned short local_buf_size)
|
||||||
|
{
|
||||||
|
STR_ASSERT(local_buf_size < 1024);
|
||||||
|
Data = local_buf();
|
||||||
|
Data[0] = '\0';
|
||||||
|
Capacity = local_buf_size ? local_buf_size - 1 : 0;
|
||||||
|
LocalBufSize = local_buf_size;
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Str::set(const char* src)
|
||||||
|
{
|
||||||
|
// We allow set(NULL) or via = operator to clear the string.
|
||||||
|
if (src == NULL)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int buf_len = (int)strlen(src);
|
||||||
|
if (Capacity < buf_len)
|
||||||
|
reserve_discard(buf_len);
|
||||||
|
memcpy(Data, src, (size_t)(buf_len + 1));
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Str::set(const char* src, const char* src_end)
|
||||||
|
{
|
||||||
|
STR_ASSERT(src != NULL && src_end >= src);
|
||||||
|
int buf_len = (int)(src_end - src);
|
||||||
|
if ((int)Capacity < buf_len)
|
||||||
|
reserve_discard(buf_len);
|
||||||
|
memcpy(Data, src, (size_t)buf_len);
|
||||||
|
Data[buf_len] = 0;
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Str::set(const Str& src)
|
||||||
|
{
|
||||||
|
int buf_len = (int)strlen(src.c_str());
|
||||||
|
if ((int)Capacity < buf_len)
|
||||||
|
reserve_discard(buf_len);
|
||||||
|
memcpy(Data, src.c_str(), (size_t)(buf_len + 1));
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Str::set(int count, char character) {
|
||||||
|
int buf_len = count + 1;
|
||||||
|
if ((int)Capacity < buf_len)
|
||||||
|
reserve_discard(buf_len);
|
||||||
|
memset(Data, character, count);
|
||||||
|
Data[count] = 0;
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Str::set_ref(const char* src)
|
||||||
|
{
|
||||||
|
if (Owned && !is_using_local_buf())
|
||||||
|
STR_MEMFREE(Data);
|
||||||
|
Data = src ? (char*)src : EmptyBuffer;
|
||||||
|
Capacity = 0;
|
||||||
|
Owned = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Str::Str()
|
||||||
|
{
|
||||||
|
Data = EmptyBuffer; // Shared READ-ONLY initial buffer for 0 capacity
|
||||||
|
Capacity = 0;
|
||||||
|
LocalBufSize = 0;
|
||||||
|
Owned = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Str::Str(const Str& rhs) : Str()
|
||||||
|
{
|
||||||
|
set(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Str::Str(const char* rhs) : Str()
|
||||||
|
{
|
||||||
|
set(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Str::Str(int amount, char character) : Str() {
|
||||||
|
set(amount, character);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Literal/reference string
|
||||||
|
class StrRef : public Str
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StrRef(const char* s) : Str() { set_ref(s); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define STR_DEFINETYPE(TYPENAME, LOCALBUFSIZE) \
|
||||||
|
class TYPENAME : public Str \
|
||||||
|
{ \
|
||||||
|
char local_buf[LOCALBUFSIZE]; \
|
||||||
|
public: \
|
||||||
|
TYPENAME() : Str(LOCALBUFSIZE) {} \
|
||||||
|
TYPENAME(const Str& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||||
|
TYPENAME(const char* rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||||
|
TYPENAME(const TYPENAME& rhs) : Str(LOCALBUFSIZE) { set(rhs); } \
|
||||||
|
TYPENAME& operator=(const char* rhs) { set(rhs); return *this; } \
|
||||||
|
TYPENAME& operator=(const Str& rhs) { set(rhs); return *this; } \
|
||||||
|
TYPENAME& operator=(const TYPENAME& rhs) { set(rhs); return *this; } \
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable PVS-Studio warning V730: Not all members of a class are initialized inside the constructor (local_buf is not initialized and that is fine)
|
||||||
|
// -V:STR_DEFINETYPE:730
|
||||||
|
|
||||||
|
// Helper to define StrXXXf constructors
|
||||||
|
#define STR_DEFINETYPE_F(TYPENAME, TYPENAME_F) \
|
||||||
|
class TYPENAME_F : public TYPENAME \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
TYPENAME_F(const char* fmt, ...) : TYPENAME() { va_list args; va_start(args, fmt); setfv(fmt, args); va_end(args); } \
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wunused-private-field" // warning : private field 'local_buf' is not used
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Declaring types for common sizes here
|
||||||
|
STR_DEFINETYPE(Str16, 16)
|
||||||
|
STR_DEFINETYPE(Str30, 30)
|
||||||
|
STR_DEFINETYPE(Str64, 64)
|
||||||
|
STR_DEFINETYPE(Str128, 128)
|
||||||
|
STR_DEFINETYPE(Str256, 256)
|
||||||
|
STR_DEFINETYPE(Str512, 512)
|
||||||
|
|
||||||
|
// Declaring helper constructors to pass in format strings in one statement
|
||||||
|
STR_DEFINETYPE_F(Str16, Str16f)
|
||||||
|
STR_DEFINETYPE_F(Str30, Str30f)
|
||||||
|
STR_DEFINETYPE_F(Str64, Str64f)
|
||||||
|
STR_DEFINETYPE_F(Str128, Str128f)
|
||||||
|
STR_DEFINETYPE_F(Str256, Str256f)
|
||||||
|
STR_DEFINETYPE_F(Str512, Str512f)
|
||||||
|
|
||||||
|
#if STR_DEFINE_STR32
|
||||||
|
STR_DEFINETYPE(Str32, 32)
|
||||||
|
STR_DEFINETYPE_F(Str32, Str32f)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef STR_INCLUDED
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// IMPLEMENTATION
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef STR_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <stdio.h> // for vsnprintf
|
||||||
|
|
||||||
|
// On some platform vsnprintf() takes va_list by reference and modifies it.
|
||||||
|
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
|
||||||
|
#ifndef va_copy
|
||||||
|
#define va_copy(dest, src) (dest = src)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Static empty buffer we can point to for empty strings
|
||||||
|
// Pointing to a literal increases the like-hood of getting a crash if someone attempts to write in the empty string buffer.
|
||||||
|
char* Str::EmptyBuffer = (char*)"\0NULL";
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
void Str::clear()
|
||||||
|
{
|
||||||
|
if (Owned && !is_using_local_buf())
|
||||||
|
STR_MEMFREE(Data);
|
||||||
|
if (LocalBufSize)
|
||||||
|
{
|
||||||
|
Data = local_buf();
|
||||||
|
Data[0] = '\0';
|
||||||
|
Capacity = LocalBufSize - 1;
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Data = EmptyBuffer;
|
||||||
|
Capacity = 0;
|
||||||
|
Owned = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve memory, preserving the current of the buffer
|
||||||
|
// Capacity doesn't include the zero terminator, so reserve(5) is enough to store "hello".
|
||||||
|
void Str::reserve(int new_capacity)
|
||||||
|
{
|
||||||
|
if (new_capacity <= Capacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char* new_data;
|
||||||
|
if (new_capacity <= LocalBufSize - 1)
|
||||||
|
{
|
||||||
|
// Disowned -> LocalBuf
|
||||||
|
new_data = local_buf();
|
||||||
|
new_capacity = LocalBufSize - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Disowned or LocalBuf -> Heap
|
||||||
|
new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));
|
||||||
|
}
|
||||||
|
|
||||||
|
// string in Data might be longer than new_capacity if it wasn't owned, don't copy too much
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
strncpy_s(new_data, (size_t)new_capacity + 1, Data, (size_t)new_capacity);
|
||||||
|
#else
|
||||||
|
strncpy(new_data, Data, (size_t)new_capacity);
|
||||||
|
#endif
|
||||||
|
new_data[new_capacity] = 0;
|
||||||
|
|
||||||
|
if (Owned && !is_using_local_buf())
|
||||||
|
STR_MEMFREE(Data);
|
||||||
|
|
||||||
|
Data = new_data;
|
||||||
|
Capacity = new_capacity;
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve memory, discarding the current of the buffer (if we expect to be fully rewritten)
|
||||||
|
void Str::reserve_discard(int new_capacity)
|
||||||
|
{
|
||||||
|
if (new_capacity <= Capacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Owned && !is_using_local_buf())
|
||||||
|
STR_MEMFREE(Data);
|
||||||
|
|
||||||
|
if (new_capacity <= LocalBufSize - 1)
|
||||||
|
{
|
||||||
|
// Disowned -> LocalBuf
|
||||||
|
Data = local_buf();
|
||||||
|
Capacity = LocalBufSize - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Disowned or LocalBuf -> Heap
|
||||||
|
Data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));
|
||||||
|
Capacity = new_capacity;
|
||||||
|
}
|
||||||
|
Owned = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Str::shrink_to_fit()
|
||||||
|
{
|
||||||
|
if (!Owned || is_using_local_buf())
|
||||||
|
return;
|
||||||
|
int new_capacity = length();
|
||||||
|
if (Capacity <= new_capacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char* new_data = (char*)STR_MEMALLOC((size_t)(new_capacity + 1) * sizeof(char));
|
||||||
|
memcpy(new_data, Data, (size_t)(new_capacity + 1));
|
||||||
|
STR_MEMFREE(Data);
|
||||||
|
Data = new_data;
|
||||||
|
Capacity = new_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: merge setfv() and appendfv()?
|
||||||
|
int Str::setfv(const char* fmt, va_list args)
|
||||||
|
{
|
||||||
|
// Needed for portability on platforms where va_list are passed by reference and modified by functions
|
||||||
|
va_list args2;
|
||||||
|
va_copy(args2, args);
|
||||||
|
|
||||||
|
// MSVC returns -1 on overflow when writing, which forces us to do two passes
|
||||||
|
// FIXME-OPT: Find a way around that.
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
int len = vsnprintf(NULL, 0, fmt, args);
|
||||||
|
STR_ASSERT(len >= 0);
|
||||||
|
|
||||||
|
if (Capacity < len)
|
||||||
|
reserve_discard(len);
|
||||||
|
len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
|
||||||
|
#else
|
||||||
|
// First try
|
||||||
|
int len = vsnprintf(Owned ? Data : NULL, Owned ? (size_t)(Capacity + 1): 0, fmt, args);
|
||||||
|
STR_ASSERT(len >= 0);
|
||||||
|
|
||||||
|
if (Capacity < len)
|
||||||
|
{
|
||||||
|
reserve_discard(len);
|
||||||
|
len = vsnprintf(Data, (size_t)len + 1, fmt, args2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STR_ASSERT(Owned);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::setf(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
int len = setfv(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::setfv_nogrow(const char* fmt, va_list args)
|
||||||
|
{
|
||||||
|
STR_ASSERT(Owned);
|
||||||
|
|
||||||
|
if (Capacity == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int w = vsnprintf(Data, (size_t)(Capacity + 1), fmt, args);
|
||||||
|
Data[Capacity] = 0;
|
||||||
|
Owned = 1;
|
||||||
|
return (w == -1) ? Capacity : w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::setf_nogrow(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
int len = setfv_nogrow(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::append_from(int idx, char c)
|
||||||
|
{
|
||||||
|
int add_len = 1;
|
||||||
|
if (Capacity < idx + add_len)
|
||||||
|
reserve(idx + add_len);
|
||||||
|
Data[idx] = c;
|
||||||
|
Data[idx + add_len] = 0;
|
||||||
|
STR_ASSERT(Owned);
|
||||||
|
return add_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::append_from(int idx, const char* s, const char* s_end)
|
||||||
|
{
|
||||||
|
if (!s_end)
|
||||||
|
s_end = s + strlen(s);
|
||||||
|
int add_len = (int)(s_end - s);
|
||||||
|
if (Capacity < idx + add_len)
|
||||||
|
reserve(idx + add_len);
|
||||||
|
memcpy(Data + idx, (const void*)s, (size_t)add_len);
|
||||||
|
Data[idx + add_len] = 0; // Our source data isn't necessarily zero terminated
|
||||||
|
STR_ASSERT(Owned);
|
||||||
|
return add_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: merge setfv() and appendfv()?
|
||||||
|
int Str::appendfv_from(int idx, const char* fmt, va_list args)
|
||||||
|
{
|
||||||
|
// Needed for portability on platforms where va_list are passed by reference and modified by functions
|
||||||
|
va_list args2;
|
||||||
|
va_copy(args2, args);
|
||||||
|
|
||||||
|
// MSVC returns -1 on overflow when writing, which forces us to do two passes
|
||||||
|
// FIXME-OPT: Find a way around that.
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
int add_len = vsnprintf(NULL, 0, fmt, args);
|
||||||
|
STR_ASSERT(add_len >= 0);
|
||||||
|
|
||||||
|
if (Capacity < idx + add_len)
|
||||||
|
reserve(idx + add_len);
|
||||||
|
add_len = vsnprintf(Data + idx, add_len + 1, fmt, args2);
|
||||||
|
#else
|
||||||
|
// First try
|
||||||
|
int add_len = vsnprintf(Owned ? Data + idx : NULL, Owned ? (size_t)(Capacity + 1 - idx) : 0, fmt, args);
|
||||||
|
STR_ASSERT(add_len >= 0);
|
||||||
|
|
||||||
|
if (Capacity < idx + add_len)
|
||||||
|
{
|
||||||
|
reserve(idx + add_len);
|
||||||
|
add_len = vsnprintf(Data + idx, (size_t)add_len + 1, fmt, args2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STR_ASSERT(Owned);
|
||||||
|
return add_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::appendf_from(int idx, const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
int len = appendfv_from(idx, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::append(char c)
|
||||||
|
{
|
||||||
|
int cur_len = length();
|
||||||
|
return append_from(cur_len, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::append(const char* s, const char* s_end)
|
||||||
|
{
|
||||||
|
int cur_len = length();
|
||||||
|
return append_from(cur_len, s, s_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::appendfv(const char* fmt, va_list args)
|
||||||
|
{
|
||||||
|
int cur_len = length();
|
||||||
|
return appendfv_from(cur_len, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Str::appendf(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
int len = appendfv(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #define STR_IMPLEMENTATION
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
@ -6,8 +6,8 @@ platforms=esp32,esp32s3
|
|||||||
[app]
|
[app]
|
||||||
id=com.d49406.RadioSet
|
id=com.d49406.RadioSet
|
||||||
version=0.0.1
|
version=0.0.1
|
||||||
name=RadioSet
|
name=Radio Terminal
|
||||||
description=Receive and transmit radio messages
|
description=Receive and transmit radio packages
|
||||||
[author]
|
[author]
|
||||||
name=Dominic Hoeglinger
|
name=Dominic Hoeglinger
|
||||||
website=https://git.hoeglinger.eu
|
website=https://github.com/ByteWelder/Tactility
|
||||||
|
|||||||
@ -13,7 +13,7 @@ bool RadioDevice::setModulation(const RadioDevice::Modulation newModulation) {
|
|||||||
const auto state = getState();
|
const auto state = getState();
|
||||||
if ((state == State::PendingOn) || (state == State::On)) {
|
if ((state == State::PendingOn) || (state == State::On)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (!(canTransmit(newModulation) || canReceive(newModulation))) {
|
} else if (!((newModulation == Modulation::None) || canTransmit(newModulation) || canReceive(newModulation))) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
auto lock = mutex.asScopedLock();
|
auto lock = mutex.asScopedLock();
|
||||||
|
|||||||
@ -17,6 +17,7 @@ enum RadioState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum Modulation {
|
enum Modulation {
|
||||||
|
MODULATION_NONE,
|
||||||
MODULATION_LORA,
|
MODULATION_LORA,
|
||||||
MODULATION_FSK,
|
MODULATION_FSK,
|
||||||
MODULATION_LRFHSS
|
MODULATION_LRFHSS
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "Tactility/hal/Device.h"
|
#include "Tactility/hal/Device.h"
|
||||||
#include "Tactility/hal/radio/RadioDevice.h"
|
#include "Tactility/hal/radio/RadioDevice.h"
|
||||||
|
|
||||||
|
auto constexpr TAG = "tt_hal_radio";
|
||||||
|
|
||||||
static RadioState fromCpp(tt::hal::radio::RadioDevice::State state);
|
static RadioState fromCpp(tt::hal::radio::RadioDevice::State state);
|
||||||
static tt::hal::radio::RadioDevice::State toCpp(RadioState state);
|
static tt::hal::radio::RadioDevice::State toCpp(RadioState state);
|
||||||
@ -162,6 +163,7 @@ static RadioState fromCpp(tt::hal::radio::RadioDevice::State state) {
|
|||||||
case tt::hal::radio::RadioDevice::State::Off:
|
case tt::hal::radio::RadioDevice::State::Off:
|
||||||
return RADIO_OFF;
|
return RADIO_OFF;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", state);
|
||||||
tt_crash("Radio state not supported");
|
tt_crash("Radio state not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,12 +181,15 @@ static tt::hal::radio::RadioDevice::State toCpp(RadioState state) {
|
|||||||
case RADIO_OFF:
|
case RADIO_OFF:
|
||||||
return tt::hal::radio::RadioDevice::State::Off;
|
return tt::hal::radio::RadioDevice::State::Off;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", state);
|
||||||
tt_crash("Radio state not supported");
|
tt_crash("Radio state not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Modulation fromCpp(tt::hal::radio::RadioDevice::Modulation modulation) {
|
static Modulation fromCpp(tt::hal::radio::RadioDevice::Modulation modulation) {
|
||||||
switch (modulation) {
|
switch (modulation) {
|
||||||
|
case tt::hal::radio::RadioDevice::Modulation::None:
|
||||||
|
return MODULATION_NONE;
|
||||||
case tt::hal::radio::RadioDevice::Modulation::LoRa:
|
case tt::hal::radio::RadioDevice::Modulation::LoRa:
|
||||||
return MODULATION_LORA;
|
return MODULATION_LORA;
|
||||||
case tt::hal::radio::RadioDevice::Modulation::Fsk:
|
case tt::hal::radio::RadioDevice::Modulation::Fsk:
|
||||||
@ -192,12 +197,15 @@ static Modulation fromCpp(tt::hal::radio::RadioDevice::Modulation modulation) {
|
|||||||
case tt::hal::radio::RadioDevice::Modulation::LrFhss:
|
case tt::hal::radio::RadioDevice::Modulation::LrFhss:
|
||||||
return MODULATION_LRFHSS;
|
return MODULATION_LRFHSS;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", modulation);
|
||||||
tt_crash("Modulation not supported");
|
tt_crash("Modulation not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static tt::hal::radio::RadioDevice::Modulation toCpp(Modulation modulation) {
|
static tt::hal::radio::RadioDevice::Modulation toCpp(Modulation modulation) {
|
||||||
switch (modulation) {
|
switch (modulation) {
|
||||||
|
case MODULATION_NONE:
|
||||||
|
return tt::hal::radio::RadioDevice::Modulation::None;
|
||||||
case MODULATION_LORA:
|
case MODULATION_LORA:
|
||||||
return tt::hal::radio::RadioDevice::Modulation::LoRa;
|
return tt::hal::radio::RadioDevice::Modulation::LoRa;
|
||||||
case MODULATION_FSK:
|
case MODULATION_FSK:
|
||||||
@ -205,6 +213,7 @@ static tt::hal::radio::RadioDevice::Modulation toCpp(Modulation modulation) {
|
|||||||
case MODULATION_LRFHSS:
|
case MODULATION_LRFHSS:
|
||||||
return tt::hal::radio::RadioDevice::Modulation::LrFhss;
|
return tt::hal::radio::RadioDevice::Modulation::LrFhss;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", modulation);
|
||||||
tt_crash("Modulation not supported");
|
tt_crash("Modulation not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,6 +243,7 @@ static RadioParameter fromCpp(tt::hal::radio::RadioDevice::Parameter parameter)
|
|||||||
case tt::hal::radio::RadioDevice::Parameter::NarrowGrid:
|
case tt::hal::radio::RadioDevice::Parameter::NarrowGrid:
|
||||||
return RADIO_NARROWGRID;
|
return RADIO_NARROWGRID;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", parameter);
|
||||||
tt_crash("Parameter not supported");
|
tt_crash("Parameter not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,6 +273,7 @@ static tt::hal::radio::RadioDevice::Parameter toCpp(RadioParameter parameter) {
|
|||||||
case RADIO_NARROWGRID:
|
case RADIO_NARROWGRID:
|
||||||
return tt::hal::radio::RadioDevice::Parameter::NarrowGrid;
|
return tt::hal::radio::RadioDevice::Parameter::NarrowGrid;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", parameter);
|
||||||
tt_crash("Parameter not supported");
|
tt_crash("Parameter not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,6 +287,7 @@ static RadioParameterStatus fromCpp(tt::hal::radio::RadioDevice::ParameterStatus
|
|||||||
case tt::hal::radio::RadioDevice::ParameterStatus::Success:
|
case tt::hal::radio::RadioDevice::ParameterStatus::Success:
|
||||||
return RADIO_PARAM_SUCCESS;
|
return RADIO_PARAM_SUCCESS;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", status);
|
||||||
tt_crash("Parameter status not supported");
|
tt_crash("Parameter status not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,6 +301,7 @@ static tt::hal::radio::RadioDevice::ParameterStatus toCpp(RadioParameterStatus s
|
|||||||
case RADIO_PARAM_SUCCESS:
|
case RADIO_PARAM_SUCCESS:
|
||||||
return tt::hal::radio::RadioDevice::ParameterStatus::Success;
|
return tt::hal::radio::RadioDevice::ParameterStatus::Success;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", status);
|
||||||
tt_crash("Parameter status not supported");
|
tt_crash("Parameter status not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,6 +319,7 @@ static RadioTxState fromCpp(tt::hal::radio::RadioDevice::TransmissionState state
|
|||||||
case tt::hal::radio::RadioDevice::TransmissionState::Error:
|
case tt::hal::radio::RadioDevice::TransmissionState::Error:
|
||||||
return RADIO_TX_ERROR;
|
return RADIO_TX_ERROR;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", state);
|
||||||
tt_crash("Transmission state not supported");
|
tt_crash("Transmission state not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,6 +337,7 @@ static tt::hal::radio::RadioDevice::TransmissionState toCpp(RadioTxState state)
|
|||||||
case RADIO_TX_ERROR:
|
case RADIO_TX_ERROR:
|
||||||
return tt::hal::radio::RadioDevice::TransmissionState::Error;
|
return tt::hal::radio::RadioDevice::TransmissionState::Error;
|
||||||
default:
|
default:
|
||||||
|
TT_LOG_W(TAG, "Unknown enum \"%d\" passed!", state);
|
||||||
tt_crash("Transmission state not supported");
|
tt_crash("Transmission state not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -385,11 +385,14 @@ const esp_elfsym elf_symbols[] {
|
|||||||
ESP_ELFSYM_EXPORT(lv_color_hex),
|
ESP_ELFSYM_EXPORT(lv_color_hex),
|
||||||
ESP_ELFSYM_EXPORT(lv_color_make),
|
ESP_ELFSYM_EXPORT(lv_color_make),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_create),
|
ESP_ELFSYM_EXPORT(lv_obj_create),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_obj_clean),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_delete),
|
ESP_ELFSYM_EXPORT(lv_obj_delete),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_add_event_cb),
|
ESP_ELFSYM_EXPORT(lv_obj_add_event_cb),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_align),
|
ESP_ELFSYM_EXPORT(lv_obj_align),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_align_to),
|
ESP_ELFSYM_EXPORT(lv_obj_align_to),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_get_parent),
|
ESP_ELFSYM_EXPORT(lv_obj_get_parent),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_obj_get_child),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_obj_get_child_count),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_get_height),
|
ESP_ELFSYM_EXPORT(lv_obj_get_height),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_get_width),
|
ESP_ELFSYM_EXPORT(lv_obj_get_width),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_get_coords),
|
ESP_ELFSYM_EXPORT(lv_obj_get_coords),
|
||||||
@ -414,6 +417,7 @@ const esp_elfsym elf_symbols[] {
|
|||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_bg_image_opa),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_bg_image_opa),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_bg_image_recolor),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_bg_image_recolor),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_bg_image_recolor_opa),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_bg_image_recolor_opa),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_flex_grow),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_hor),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_hor),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_ver),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_ver),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_top),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_margin_top),
|
||||||
@ -445,6 +449,7 @@ const esp_elfsym elf_symbols[] {
|
|||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_text_outline_stroke_opa),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_text_outline_stroke_opa),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_style_text_outline_stroke_width),
|
ESP_ELFSYM_EXPORT(lv_obj_set_style_text_outline_stroke_width),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_align),
|
ESP_ELFSYM_EXPORT(lv_obj_set_align),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_obj_set_layout),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_x),
|
ESP_ELFSYM_EXPORT(lv_obj_set_x),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_y),
|
ESP_ELFSYM_EXPORT(lv_obj_set_y),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_set_size),
|
ESP_ELFSYM_EXPORT(lv_obj_set_size),
|
||||||
@ -504,6 +509,7 @@ const esp_elfsym elf_symbols[] {
|
|||||||
ESP_ELFSYM_EXPORT(lv_dropdown_get_option_count),
|
ESP_ELFSYM_EXPORT(lv_dropdown_get_option_count),
|
||||||
ESP_ELFSYM_EXPORT(lv_dropdown_get_option_index),
|
ESP_ELFSYM_EXPORT(lv_dropdown_get_option_index),
|
||||||
ESP_ELFSYM_EXPORT(lv_dropdown_get_options),
|
ESP_ELFSYM_EXPORT(lv_dropdown_get_options),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_dropdown_get_selected),
|
||||||
ESP_ELFSYM_EXPORT(lv_dropdown_set_dir),
|
ESP_ELFSYM_EXPORT(lv_dropdown_set_dir),
|
||||||
ESP_ELFSYM_EXPORT(lv_dropdown_set_options),
|
ESP_ELFSYM_EXPORT(lv_dropdown_set_options),
|
||||||
ESP_ELFSYM_EXPORT(lv_dropdown_set_options_static),
|
ESP_ELFSYM_EXPORT(lv_dropdown_set_options_static),
|
||||||
@ -581,6 +587,38 @@ const esp_elfsym elf_symbols[] {
|
|||||||
ESP_ELFSYM_EXPORT(lv_obj_get_style_grid_cell_row_span),
|
ESP_ELFSYM_EXPORT(lv_obj_get_style_grid_cell_row_span),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_get_style_grid_cell_x_align),
|
ESP_ELFSYM_EXPORT(lv_obj_get_style_grid_cell_x_align),
|
||||||
ESP_ELFSYM_EXPORT(lv_obj_get_style_grid_cell_y_align),
|
ESP_ELFSYM_EXPORT(lv_obj_get_style_grid_cell_y_align),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_group_focus_obj),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_group_get_default),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_group_add_obj),
|
||||||
|
// lv_style
|
||||||
|
ESP_ELFSYM_EXPORT(lv_style_init),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_style_set_bg_color),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_style_set_bg_opa),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_style_set_border_width),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_style_set_border_color),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_color_black),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_obj_add_style),
|
||||||
|
// lv_slider
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_create),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_set_value),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_set_start_value),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_set_range),
|
||||||
|
//ESP_ELFSYM_EXPORT(lv_slider_set_min_value),
|
||||||
|
//ESP_ELFSYM_EXPORT(lv_slider_set_max_value),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_set_mode),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_set_orientation),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_get_value),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_get_left_value),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_get_min_value),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_get_max_value),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_is_dragged),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_get_mode),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_get_orientation),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_is_symmetrical),
|
||||||
|
ESP_ELFSYM_EXPORT(lv_slider_bind_value),
|
||||||
|
//lv_snprintf
|
||||||
|
ESP_ELFSYM_EXPORT(lv_snprintf),
|
||||||
|
|
||||||
// delimiter
|
// delimiter
|
||||||
ESP_ELFSYM_END
|
ESP_ELFSYM_END
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user