mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-17 04:34:44 -07:00
added multiconverter / removed hid analyzer
hid analyzer won't needed anymore because of new lfrfid system adds support for multiple hid protocols
This commit is contained in:
10
applications/multi_converter/application.fam
Normal file
10
applications/multi_converter/application.fam
Normal file
@@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="multi_converter",
|
||||
name="Multi Converter",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="multi_converter_app",
|
||||
cdefines=["APP_DEC_HEX_CONVERTER"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=19,
|
||||
)
|
||||
162
applications/multi_converter/multi_converter.c
Normal file
162
applications/multi_converter/multi_converter.c
Normal file
@@ -0,0 +1,162 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "multi_converter_definitions.h"
|
||||
#include "multi_converter_mode_display.h"
|
||||
#include "multi_converter_mode_select.h"
|
||||
|
||||
static void multi_converter_render_callback(Canvas* const canvas, void* ctx) {
|
||||
|
||||
const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(multi_converter_state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (multi_converter_state->mode == ModeDisplay) {
|
||||
multi_converter_mode_display_draw(canvas, multi_converter_state);
|
||||
} else {
|
||||
multi_converter_mode_select_draw(canvas, multi_converter_state);
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, multi_converter_state);
|
||||
}
|
||||
|
||||
static void multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void multi_converter_init(MultiConverterState* const multi_converter_state) {
|
||||
// initial default values
|
||||
|
||||
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0';
|
||||
multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators
|
||||
|
||||
multi_converter_state->unit_type_orig = UnitTypeDec;
|
||||
multi_converter_state->unit_type_dest = UnitTypeHex;
|
||||
|
||||
multi_converter_state->keyboard_lock = 0;
|
||||
|
||||
// init the display view
|
||||
multi_converter_mode_display_reset(multi_converter_state);
|
||||
|
||||
// init the select view
|
||||
multi_converter_mode_select_reset(multi_converter_state);
|
||||
|
||||
// set ModeDisplay as the current mode
|
||||
multi_converter_state->mode = ModeDisplay;
|
||||
}
|
||||
|
||||
// main entry point
|
||||
int32_t multi_converter_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// get event queue
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent));
|
||||
|
||||
// allocate state
|
||||
MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState));
|
||||
|
||||
// set mutex for plugin state (different threads can access it)
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) {
|
||||
FURI_LOG_E("MultiConverter", "cannot create mutex\r\n");
|
||||
free(multi_converter_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
// register callbacks for drawing and input processing
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue);
|
||||
|
||||
// open GUI and register view_port
|
||||
Gui* gui = furi_record_open("gui");
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
multi_converter_init(multi_converter_state);
|
||||
|
||||
// main loop
|
||||
MultiConverterEvent event;
|
||||
for (bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
MultiConverterState* multi_converter_state = (MultiConverterState*)acquire_mutex_block(&state_mutex);
|
||||
|
||||
if (event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if (event.type == EventTypeKey && !multi_converter_state->keyboard_lock) {
|
||||
if (multi_converter_state->mode == ModeDisplay) {
|
||||
|
||||
if (event.input.key == InputKeyBack) {
|
||||
if (event.input.type == InputTypePress) processing = false;
|
||||
} else if (event.input.key == InputKeyOk) { // the "ok" press can be short or long
|
||||
MultiConverterModeTrigger t = None;
|
||||
|
||||
if (event.input.type == InputTypeLong) t = multi_converter_mode_display_ok(1, multi_converter_state);
|
||||
else if (event.input.type == InputTypeShort) t = multi_converter_mode_display_ok(0, multi_converter_state);
|
||||
|
||||
if (t == Reset) {
|
||||
multi_converter_mode_select_reset(multi_converter_state);
|
||||
multi_converter_state->mode = ModeSelector;
|
||||
}
|
||||
} else {
|
||||
if (event.input.type == InputTypePress) multi_converter_mode_display_navigation(event.input.key, multi_converter_state);
|
||||
}
|
||||
|
||||
} else { // ModeSelect
|
||||
if (event.input.type == InputTypePress) {
|
||||
switch (event.input.key) {
|
||||
default:
|
||||
break;
|
||||
case InputKeyBack:
|
||||
case InputKeyOk: {
|
||||
MultiConverterModeTrigger t = multi_converter_mode_select_exit(event.input.key == InputKeyOk ? 1 : 0, multi_converter_state);
|
||||
|
||||
if (t == Reset) {
|
||||
multi_converter_mode_display_reset(multi_converter_state);
|
||||
} else if (t == Convert) {
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
}
|
||||
|
||||
multi_converter_state->keyboard_lock = 1;
|
||||
multi_converter_state->mode = ModeDisplay;
|
||||
break;
|
||||
}
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
multi_converter_mode_select_switch(multi_converter_state);
|
||||
break;
|
||||
case InputKeyUp:
|
||||
multi_converter_mode_select_change_unit(-1, multi_converter_state);
|
||||
break;
|
||||
case InputKeyDown:
|
||||
multi_converter_mode_select_change_unit(1, multi_converter_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (multi_converter_state->keyboard_lock) {
|
||||
multi_converter_state->keyboard_lock = 0;
|
||||
}
|
||||
} else {
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, multi_converter_state);
|
||||
}
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close("gui");
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
free(multi_converter_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
82
applications/multi_converter/multi_converter_definitions.h
Normal file
82
applications/multi_converter/multi_converter_definitions.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#define MULTI_CONVERTER_NUMBER_DIGITS 9
|
||||
|
||||
typedef enum {
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
InputEvent input;
|
||||
EventType type;
|
||||
} MultiConverterEvent;
|
||||
|
||||
typedef enum {
|
||||
ModeDisplay,
|
||||
ModeSelector,
|
||||
} MultiConverterMode;
|
||||
|
||||
typedef enum {
|
||||
None,
|
||||
Reset,
|
||||
Convert,
|
||||
} MultiConverterModeTrigger;
|
||||
|
||||
// new units goes here, used as index to the main multi_converter_available_units array (multi_converter_units.h)
|
||||
typedef enum {
|
||||
UnitTypeDec,
|
||||
UnitTypeHex,
|
||||
UnitTypeBin,
|
||||
|
||||
UnitTypeCelsius,
|
||||
UnitTypeFahernheit,
|
||||
UnitTypeKelvin,
|
||||
|
||||
UnitTypeKilometers,
|
||||
UnitTypeMeters,
|
||||
UnitTypeCentimeters,
|
||||
UnitTypeMiles,
|
||||
UnitTypeFeet,
|
||||
UnitTypeInches,
|
||||
|
||||
UnitTypeDegree,
|
||||
UnitTypeRadian,
|
||||
} MultiConverterUnitType;
|
||||
|
||||
typedef struct {
|
||||
MultiConverterUnitType selected_unit_type_orig;
|
||||
MultiConverterUnitType selected_unit_type_dest;
|
||||
uint8_t select_orig;
|
||||
} MultiConverterModeSelect;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cursor; // cursor position when typing
|
||||
int8_t key; // hover key
|
||||
uint8_t comma; // comma already added? (only one comma allowed)
|
||||
uint8_t negative; // is negative?
|
||||
} MultiConverterModeDisplay;
|
||||
|
||||
typedef struct MultiConverterUnit MultiConverterUnit;
|
||||
typedef struct MultiConverterState MultiConverterState;
|
||||
|
||||
struct MultiConverterUnit {
|
||||
uint8_t allow_comma;
|
||||
uint8_t allow_negative;
|
||||
uint8_t max_number_keys;
|
||||
char mini_name[4];
|
||||
char name[12];
|
||||
void (*convert_function)(MultiConverterState * const);
|
||||
uint8_t (*allowed_function)(MultiConverterUnitType);
|
||||
};
|
||||
|
||||
struct MultiConverterState {
|
||||
char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1];
|
||||
char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1];
|
||||
MultiConverterUnitType unit_type_orig;
|
||||
MultiConverterUnitType unit_type_dest;
|
||||
MultiConverterMode mode;
|
||||
MultiConverterModeDisplay display;
|
||||
MultiConverterModeSelect select;
|
||||
uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes
|
||||
// (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!)
|
||||
};
|
||||
284
applications/multi_converter/multi_converter_mode_display.c
Normal file
284
applications/multi_converter/multi_converter_mode_display.c
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "multi_converter_mode_display.h"
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT]
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#'
|
||||
#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' '
|
||||
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3
|
||||
#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8
|
||||
|
||||
|
||||
void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state) {
|
||||
|
||||
// 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...)
|
||||
// just copy buffer_orig to buffer_dest and that's it
|
||||
|
||||
if (multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) {
|
||||
memcpy(multi_converter_state->buffer_dest, multi_converter_state->buffer_orig, MULTI_CONVERTER_NUMBER_DIGITS);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2.- origin_buffer has not null functions
|
||||
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) return;
|
||||
|
||||
// 3.- valid destination type (using allowed_destinations function)
|
||||
if (!multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function(multi_converter_state->unit_type_dest)) return;
|
||||
|
||||
multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function(multi_converter_state);
|
||||
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// ORIGIN
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig);
|
||||
|
||||
// DESTINATION
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10 + 12, multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest);
|
||||
|
||||
// SEPARATOR_LINE
|
||||
canvas_draw_line(canvas, 2, 25, 128 - 3, 25);
|
||||
|
||||
// KEYBOARD
|
||||
uint8_t _x = 5;
|
||||
uint8_t _y = 25 + 15; // line + 10
|
||||
|
||||
for (int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) {
|
||||
|
||||
char g;
|
||||
if (i < 10) g = (i + '0');
|
||||
else if (i < 16) g = ((i - 10) + 'A');
|
||||
else if (i == MULTI_CONVERTER_DISPLAY_KEY_DEL) g = MULTI_CONVERTER_DISPLAY_CHAR_DEL;
|
||||
else g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT;
|
||||
|
||||
uint8_t g_w = canvas_glyph_width(canvas, g);
|
||||
|
||||
if (i < 16 && i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
|
||||
// some units won't use the full [0] - [F] keyboard, in those situations just hide the char
|
||||
// (won't be selectable anyway, so no worries here; this is just about drawing stuff)
|
||||
g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
}
|
||||
|
||||
// currently hover key is highlighted
|
||||
if ((multi_converter_state->display).key == i) {
|
||||
canvas_draw_box(canvas,
|
||||
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
|
||||
);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_draw_frame(canvas,
|
||||
_x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
_y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN),
|
||||
MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN,
|
||||
MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2
|
||||
);
|
||||
}
|
||||
|
||||
// draw key
|
||||
canvas_draw_glyph(canvas, _x, _y, g);
|
||||
|
||||
// certain keys have long_press features, draw whatever they're using there too
|
||||
if (i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
|
||||
canvas_draw_box(canvas,
|
||||
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4,
|
||||
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
|
||||
4,
|
||||
2
|
||||
);
|
||||
} else if (i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
|
||||
canvas_draw_box(canvas,
|
||||
_x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2,
|
||||
_y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2,
|
||||
2,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
// back to black
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if (i < 8) {
|
||||
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2;
|
||||
} else if (i == 8) {
|
||||
_y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + 3;
|
||||
_x = 8; // some padding at the beginning on second line
|
||||
} else {
|
||||
_x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state) {
|
||||
|
||||
// first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one"
|
||||
switch (key) {
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
case InputKeyUp:
|
||||
case InputKeyDown:
|
||||
if ((multi_converter_state->display).key >= 9) (multi_converter_state->display).key -= 9;
|
||||
else (multi_converter_state->display).key += 9;
|
||||
break;
|
||||
|
||||
case InputKeyLeft:
|
||||
case InputKeyRight:
|
||||
|
||||
(multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1);
|
||||
|
||||
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
|
||||
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
|
||||
break;
|
||||
}
|
||||
|
||||
// if destination key is disabled by max_number_keys, move to the closest one
|
||||
// (this could be improved with more accurate keys movements, probably...)
|
||||
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) return; // weird, since this means "do not show any number on the keyboard, but just in case..."
|
||||
|
||||
int8_t i = -1;
|
||||
if (key == InputKeyRight || key == InputKeyDown) i = 1;
|
||||
|
||||
while ((multi_converter_state->display).key < 16 && (multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) {
|
||||
(multi_converter_state->display).key += i;
|
||||
if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0;
|
||||
else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state) {
|
||||
|
||||
// clean the buffers
|
||||
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) {
|
||||
multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
}
|
||||
|
||||
// reset the display flags and index
|
||||
multi_converter_state->display.cursor = 0;
|
||||
multi_converter_state->display.key = 0;
|
||||
multi_converter_state->display.comma = 0;
|
||||
multi_converter_state->display.negative = 0;
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state) {
|
||||
if (multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) {
|
||||
|
||||
if (!(multi_converter_state->display).negative) {
|
||||
// shift origin buffer one to right + add the "-" sign (last digit will be lost)
|
||||
for (int i = MULTI_CONVERTER_NUMBER_DIGITS-1; i > 0; i--) {
|
||||
// we could avoid the blanks, but nevermind
|
||||
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i-1];
|
||||
}
|
||||
multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE;
|
||||
|
||||
// only increment cursor if we're not out of bound
|
||||
if ((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) (multi_converter_state->display).cursor++;
|
||||
} else {
|
||||
// shift origin buffer one to left, append ' ' on the end
|
||||
for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS-1; i++) {
|
||||
if (multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) break;
|
||||
|
||||
multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i+1];
|
||||
}
|
||||
multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS-1] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
|
||||
(multi_converter_state->display).cursor--;
|
||||
}
|
||||
|
||||
// toggle flag
|
||||
(multi_converter_state->display).negative ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state) {
|
||||
if (
|
||||
!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma ||
|
||||
(multi_converter_state->display).comma ||
|
||||
!(multi_converter_state->display).cursor ||
|
||||
((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1))
|
||||
) return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars
|
||||
|
||||
// set flag to one
|
||||
(multi_converter_state->display).comma = 1;
|
||||
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_COMMA;
|
||||
(multi_converter_state->display).cursor++;
|
||||
}
|
||||
|
||||
void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) {
|
||||
if ((multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) return;
|
||||
|
||||
if ((multi_converter_state->display).key < 10) {
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = (multi_converter_state->display).key + '0';
|
||||
} else {
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = ((multi_converter_state->display).key - 10) + 'A';
|
||||
}
|
||||
|
||||
(multi_converter_state->display).cursor++;
|
||||
}
|
||||
|
||||
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state) {
|
||||
|
||||
if ((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) {
|
||||
if ((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) return None; // limit reached, ignore
|
||||
|
||||
// long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed
|
||||
if (long_press) {
|
||||
|
||||
if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) {
|
||||
// toggle negative
|
||||
multi_converter_mode_display_toggle_negative(multi_converter_state);
|
||||
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) {
|
||||
// add comma
|
||||
multi_converter_mode_display_add_comma(multi_converter_state);
|
||||
}
|
||||
|
||||
} else {
|
||||
// regular keys
|
||||
multi_converter_mode_display_add_number(multi_converter_state);
|
||||
}
|
||||
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
|
||||
} else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) {
|
||||
if ((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--;
|
||||
|
||||
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_COMMA) (multi_converter_state->display).comma = 0;
|
||||
if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) (multi_converter_state->display).negative = 0;
|
||||
|
||||
multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK;
|
||||
|
||||
multi_converter_mode_display_convert(multi_converter_state);
|
||||
|
||||
} else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT
|
||||
return Reset;
|
||||
}
|
||||
|
||||
return None;
|
||||
|
||||
}
|
||||
55
applications/multi_converter/multi_converter_mode_display.h
Normal file
55
applications/multi_converter/multi_converter_mode_display.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
#include "multi_converter_definitions.h"
|
||||
#include "multi_converter_units.h"
|
||||
|
||||
//
|
||||
// performs a unit conversion from origin to source buffers, if there's any error, overflow or
|
||||
// non-compatible format (which shouldn't happen, but just in case) abort conversion and outputs
|
||||
// some "?" strings on the buffer or something similar
|
||||
//
|
||||
void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// draw the main DISPLAY view with the current multi_converter_state values
|
||||
//
|
||||
void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
|
||||
|
||||
//
|
||||
// keyboard navigation on DISPLAY mode (NAVIGATION only, no BACK nor OK - InputKey guaranteed to be left/right/up/down)
|
||||
//
|
||||
void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// reset the DISPLAY mode with the current units, cleaning the buffers and different flags;
|
||||
// call this when exiting the SELECT mode / changing the units
|
||||
//
|
||||
void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// toggle the negative flag on current selected buffer ONLY if the unit allows negative numbers
|
||||
// (adding negative number may crop the last char on the buffer; it cannot be recovered)
|
||||
//
|
||||
void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// add a comma/dot/decimal separator/whatever on current selected buffer ONLY if the unit allows it
|
||||
// (only ONE comma allowed, not in the beginning nor end)
|
||||
//
|
||||
void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// add a regular number to the buffer if it's <= the max_number_keys from the unit (not necessary
|
||||
// since the draw and navigation functions won't allow a trigger for an invalid number, but still
|
||||
// to keep the "checks" policy on each "add key" function...)
|
||||
//
|
||||
void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// handle the OK action when selecting a specific key on the keyboard (add a number, a symbol, change mode...)
|
||||
// returns a ModeTrigger enum value: may or may not let to a mode change on the main loop (WON'T change the mode here)
|
||||
//
|
||||
MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state);
|
||||
160
applications/multi_converter/multi_converter_mode_select.c
Normal file
160
applications/multi_converter/multi_converter_mode_select.c
Normal file
@@ -0,0 +1,160 @@
|
||||
#include "multi_converter_mode_select.h"
|
||||
|
||||
#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3
|
||||
|
||||
#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:"
|
||||
#define MULTI_CONVERTER_INFO_STRING_TO "TO:"
|
||||
#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change"
|
||||
#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel"
|
||||
|
||||
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state) {
|
||||
int i = 1;
|
||||
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...)
|
||||
int ut = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_dest, i * d);
|
||||
if (
|
||||
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(ut) &&
|
||||
(multi_converter_state->select).selected_unit_type_orig != ut
|
||||
) {
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas) {
|
||||
canvas_draw_box(canvas, x - 2 , y - 10, canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) {
|
||||
|
||||
int y = 10;
|
||||
int x = 10;
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// FROM
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// offset -1
|
||||
y += 12;
|
||||
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, -1) ].name);
|
||||
|
||||
// current selected element
|
||||
y += 12;
|
||||
|
||||
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_orig, canvas);
|
||||
|
||||
if ((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
|
||||
|
||||
// offset +1
|
||||
y += 12;
|
||||
|
||||
canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, 1) ].name);
|
||||
|
||||
// TO
|
||||
y = 10;
|
||||
x = 70;
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
// offset -1: go back from current selected destination and find the first one valid (even if it's itself)
|
||||
y += 12;
|
||||
|
||||
multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state);
|
||||
|
||||
// current selected element
|
||||
y += 12;
|
||||
|
||||
multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_dest, canvas);
|
||||
|
||||
if (!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">");
|
||||
|
||||
// offset +1: same but on the opposite direction
|
||||
y += 12;
|
||||
|
||||
multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state);
|
||||
|
||||
// OK / CANCEL
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12);
|
||||
canvas_draw_box(canvas, 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 12, canvas_string_width(canvas, "BACK: Cancel") + 4, 12);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK);
|
||||
canvas_draw_str(canvas, 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 3, MULTI_CONVERTER_INFO_STRING_BACK);
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state) {
|
||||
|
||||
// initial pre-selected values are equal to the current selected values
|
||||
(multi_converter_state->select).selected_unit_type_orig = multi_converter_state->unit_type_orig;
|
||||
(multi_converter_state->select).selected_unit_type_dest = multi_converter_state->unit_type_dest;
|
||||
|
||||
(multi_converter_state->select).select_orig = 1;
|
||||
}
|
||||
|
||||
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state) {
|
||||
if (save_changes) {
|
||||
|
||||
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
|
||||
|
||||
if (multi_converter_state->unit_type_orig == (multi_converter_state->select).selected_unit_type_orig) {
|
||||
// if the ORIGIN unit didn't changed, just trigger the convert
|
||||
|
||||
return Convert;
|
||||
} else {
|
||||
multi_converter_state->unit_type_orig = (multi_converter_state->select).selected_unit_type_orig;
|
||||
multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest;
|
||||
|
||||
return Reset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state) {
|
||||
(multi_converter_state->select).select_orig ^= 1;
|
||||
}
|
||||
|
||||
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state) {
|
||||
|
||||
MultiConverterUnitType d;
|
||||
if ((multi_converter_state->select).select_orig) {
|
||||
(multi_converter_state->select).selected_unit_type_orig = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, direction);
|
||||
d = (multi_converter_state->select).selected_unit_type_dest;
|
||||
} else {
|
||||
d = ((multi_converter_state->select).selected_unit_type_dest + direction) % MULTI_CONVERTER_AVAILABLE_UNITS;
|
||||
}
|
||||
|
||||
// check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION
|
||||
// (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME);
|
||||
// also notice that ORIGIN must be DIFFERENT than DESTINATION
|
||||
int i = 0;
|
||||
while (i < MULTI_CONVERTER_AVAILABLE_UNITS) {
|
||||
if (
|
||||
multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(d) &&
|
||||
(multi_converter_state->select).selected_unit_type_orig != d
|
||||
) {
|
||||
(multi_converter_state->select).selected_unit_type_dest = d;
|
||||
break;
|
||||
}
|
||||
|
||||
d = multi_converter_get_unit_type_offset(d, direction);
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
58
applications/multi_converter/multi_converter_mode_select.h
Normal file
58
applications/multi_converter/multi_converter_mode_select.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
#include "multi_converter_definitions.h"
|
||||
#include "multi_converter_units.h"
|
||||
|
||||
//
|
||||
// aux draw function for units offsets and draw stuff
|
||||
//
|
||||
void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state);
|
||||
|
||||
void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas);
|
||||
|
||||
//
|
||||
// draw the main SELECT view with the current multi_converter_state values
|
||||
//
|
||||
void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state);
|
||||
|
||||
//
|
||||
// reset the SELECT mode view, showing as "pre-selected" the current working units
|
||||
//
|
||||
void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// exit from SELECT mode and go back to display view, if save_changes == 1 use the current SELECT view info
|
||||
// to modify the current selected units and reset the views properly (usually if the ORIGIN unit has been
|
||||
// changed, reset everything; otherwise just trigger the convert function with a new DESTINATION)
|
||||
//
|
||||
// currently this function DON'T CHECK invalid unit relations (the navigation and display functions will
|
||||
// prevent weird behaviours, so for now we're trusting the selected_unit_orig/dest_type values)
|
||||
//
|
||||
// returns an enum code MultiConverterDisplayTrigger based on doing nothing (cancel), triggering the display
|
||||
// convert method or reseting the whole display mode (when fully changing the units)
|
||||
//
|
||||
// notice the MODE CHANGE itself is not done here but in the main loop (outside the call) via the ModeTrigger enum element
|
||||
//
|
||||
MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// switch between selecting the ORIGIN or the DESTINATION unit on DISPLAY mode (since there're only
|
||||
// two options, both left/right arrow keys acts as toggles, no "direction" required)
|
||||
//
|
||||
void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state);
|
||||
|
||||
//
|
||||
// change the selected unit on SELECTED mode, using the select_orig flag to check if we're switching the
|
||||
// ORIGIN or the DESTINATION unit; the DIRECTION (up or down to travel the array) is set as a param
|
||||
//
|
||||
// when switching the ORIGIN one, reset the DESTINATION to the first valid unit (if the current one is not
|
||||
// valid anymore); when switching the DESTINATION one, an allowed_function() check is performed in order to
|
||||
// properly set a valid destination unit.
|
||||
//
|
||||
// (notice the draw step also perform which units are valid to display, so no worries about that here)
|
||||
//
|
||||
void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state);
|
||||
230
applications/multi_converter/multi_converter_units.c
Normal file
230
applications/multi_converter/multi_converter_units.c
Normal file
@@ -0,0 +1,230 @@
|
||||
#include "multi_converter_units.h"
|
||||
|
||||
#define MULTI_CONVERTER_CHAR_OVERFLOW '#'
|
||||
#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999
|
||||
|
||||
#define multi_converter_unit_set_overflow(b) for (int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW;
|
||||
|
||||
//
|
||||
// DEC / HEX / BIN conversion
|
||||
//
|
||||
void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state) {
|
||||
|
||||
char dest[MULTI_CONVERTER_NUMBER_DIGITS];
|
||||
|
||||
int i = 0;
|
||||
uint8_t overflow = 0;
|
||||
|
||||
int a = 0;
|
||||
int r = 0;
|
||||
uint8_t f = 1;
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeDec: {
|
||||
a = atoi(multi_converter_state->buffer_orig);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2);
|
||||
|
||||
break;
|
||||
}
|
||||
case UnitTypeHex:
|
||||
a = strtol(multi_converter_state->buffer_orig, NULL, 16);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2);
|
||||
|
||||
break;
|
||||
case UnitTypeBin:
|
||||
a = strtol(multi_converter_state->buffer_orig, NULL, 2);
|
||||
f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
while (a > 0) {
|
||||
r = a % f;
|
||||
dest[i] = r + (r < 10 ? '0' : ('A' - 10) );
|
||||
a /= f;
|
||||
if (i++ >= MULTI_CONVERTER_NUMBER_DIGITS) {
|
||||
overflow = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
// copy DEST (reversed) to destination and append empty chars at the end
|
||||
for (int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) {
|
||||
if (i >= 1) multi_converter_state->buffer_dest[j] = dest[--i];
|
||||
else multi_converter_state->buffer_dest[j] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType unit_type) {
|
||||
return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin);
|
||||
}
|
||||
|
||||
//
|
||||
// CEL / FAR / KEL
|
||||
//
|
||||
void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state) {
|
||||
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeCelsius:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
|
||||
// celsius to fahrenheit
|
||||
a = (a * ((double) 1.8)) + 32;
|
||||
} else { // UnitTypeKelvin
|
||||
a += ((double) 273.15);
|
||||
}
|
||||
|
||||
break;
|
||||
case UnitTypeFahernheit:
|
||||
// fahrenheit to celsius, always
|
||||
a = (a - 32) / ((double) 1.8);
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKelvin) {
|
||||
// if kelvin, add
|
||||
a += ((double) 273.15);
|
||||
}
|
||||
|
||||
break;
|
||||
case UnitTypeKelvin:
|
||||
// kelvin to celsius, always
|
||||
a -= ((double) 273.15);
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) {
|
||||
// if fahernheit, convert
|
||||
a = (a * ((double) 1.8)) + 32;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
|
||||
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a);
|
||||
|
||||
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_type) {
|
||||
return (unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || unit_type == UnitTypeKelvin);
|
||||
}
|
||||
|
||||
//
|
||||
// KM / M / CM / MILES / FEET / INCHES
|
||||
//
|
||||
|
||||
void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state) {
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeKilometers:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.6213711);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3280.839895013);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39370.078740157);
|
||||
break;
|
||||
case UnitTypeMeters:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 1000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0006213711);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3.280839895013);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39.370078740157);
|
||||
break;
|
||||
case UnitTypeCentimeters:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 100000);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a /= ((double) 100);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000006213711);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.03280839895013);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 0.39370078740157);
|
||||
break;
|
||||
|
||||
case UnitTypeMiles:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 1.609344);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1609.344);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 160934.4);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 5280);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 63360);
|
||||
break;
|
||||
case UnitTypeFeet:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0003048);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.3048);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 30.48);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000189393939394);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 12);
|
||||
break;
|
||||
case UnitTypeInches:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0000254);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.0254);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 2.54);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0000157828282828);
|
||||
else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.0833333333333);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
|
||||
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
|
||||
|
||||
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) {
|
||||
return (
|
||||
unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || unit_type == UnitTypeCentimeters ||
|
||||
unit_type == UnitTypeMiles || unit_type == UnitTypeFeet || unit_type == UnitTypeInches
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// DEG / RAD
|
||||
//
|
||||
|
||||
void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state) {
|
||||
double a = strtof(multi_converter_state->buffer_orig, NULL);
|
||||
uint8_t overflow = 0;
|
||||
|
||||
switch(multi_converter_state->unit_type_orig) {
|
||||
default:
|
||||
break;
|
||||
case UnitTypeDegree:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double) 0.0174532925199);
|
||||
break;
|
||||
|
||||
case UnitTypeRadian:
|
||||
if (multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double) 57.2957795131);
|
||||
break;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
} else {
|
||||
|
||||
int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a);
|
||||
|
||||
if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type) {
|
||||
return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian);
|
||||
}
|
||||
71
applications/multi_converter/multi_converter_units.h
Normal file
71
applications/multi_converter/multi_converter_units.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <input/input.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
#include "multi_converter_definitions.h"
|
||||
|
||||
#define MULTI_CONVERTER_AVAILABLE_UNITS 14
|
||||
|
||||
#define multi_converter_get_unit(unit_type) multi_converter_available_units[unit_type]
|
||||
#define multi_converter_get_unit_type_offset(unit_type, offset) (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % MULTI_CONVERTER_AVAILABLE_UNITS)
|
||||
// the modulo operation will fail with extremely large values on the units array
|
||||
|
||||
// DEC / HEX / BIN
|
||||
void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state);
|
||||
uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType);
|
||||
|
||||
// CEL / FAR / KEL
|
||||
void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state);
|
||||
uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType);
|
||||
|
||||
// KM / M / CM / MILES / FEET / INCHES
|
||||
void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state);
|
||||
uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType);
|
||||
|
||||
// DEG / RAD
|
||||
void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state);
|
||||
uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type);
|
||||
|
||||
//
|
||||
// each unit is made of comma? + negative? + keyboard_length + mini_name + name + convert function + allowed function
|
||||
// (setting functions as NULL will cause convert / select options to be ignored)
|
||||
//
|
||||
static const MultiConverterUnit multi_converter_unit_dec = { 0, 0, 10, "DEC\0", "Decimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_hex = { 0, 0, 16, "HEX\0", "Hexadecimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_bin = { 0, 0, 2, "BIN\0", "Binary\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed };
|
||||
|
||||
static const MultiConverterUnit multi_converter_unit_cel = { 1, 1, 10, "CEL\0", "Celsius\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_far = { 1, 1, 10, "FAR\0", "Fahernheit\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_kel = { 1, 1, 10, "KEL\0", "Kelvin\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed };
|
||||
|
||||
static const MultiConverterUnit multi_converter_unit_km = { 1, 0, 10, "KM\0", "Kilometers\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_m = { 1, 0, 10, "M\0", "Meters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_cm = { 1, 0, 10, "CM\0", "Centimeters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_mi = { 1, 0, 10, "MI\0", "Miles\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_ft = { 1, 0, 10, "FT\0", "Feet\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_in = { 1, 0, 10, " \"\0", "Inches\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed };
|
||||
|
||||
static const MultiConverterUnit multi_converter_unit_deg = { 1, 0, 10, "DEG\0", "Degree\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
|
||||
static const MultiConverterUnit multi_converter_unit_rad = { 1, 0, 10, "RAD\0", "Radian\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed };
|
||||
|
||||
// index order set by the MultiConverterUnitType enum element (multi_converter_definitions.h)
|
||||
static const MultiConverterUnit multi_converter_available_units[MULTI_CONVERTER_AVAILABLE_UNITS] = {
|
||||
[UnitTypeDec] = multi_converter_unit_dec,
|
||||
[UnitTypeHex] = multi_converter_unit_hex,
|
||||
[UnitTypeBin] = multi_converter_unit_bin,
|
||||
|
||||
[UnitTypeCelsius] = multi_converter_unit_cel,
|
||||
[UnitTypeFahernheit] = multi_converter_unit_far,
|
||||
[UnitTypeKelvin] = multi_converter_unit_kel,
|
||||
|
||||
[UnitTypeKilometers] = multi_converter_unit_km,
|
||||
[UnitTypeMeters] = multi_converter_unit_m,
|
||||
[UnitTypeCentimeters] = multi_converter_unit_cm,
|
||||
[UnitTypeMiles] = multi_converter_unit_mi,
|
||||
[UnitTypeFeet] = multi_converter_unit_ft,
|
||||
[UnitTypeInches] = multi_converter_unit_in,
|
||||
|
||||
[UnitTypeDegree] = multi_converter_unit_deg,
|
||||
[UnitTypeRadian] = multi_converter_unit_rad,
|
||||
};
|
||||
Reference in New Issue
Block a user