diff --git a/meson.build b/meson.build index a0288055..cf1ea48a 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,7 @@ project('OpenRTX', 'c', openrtx_src = ['openrtx/src/bootstrap.c', 'openrtx/src/state.c', 'openrtx/src/ui.c', + 'openrtx/src/ui_menu.c', 'openrtx/src/threads.c', 'openrtx/src/battery.c', 'openrtx/src/graphics.c', diff --git a/openrtx/include/ui.h b/openrtx/include/ui.h index 2bf3091e..11dfbd30 100644 --- a/openrtx/include/ui.h +++ b/openrtx/include/ui.h @@ -22,9 +22,18 @@ #include #include +#include #include #include #include +#include + +// Maximum menu entry length +#define MAX_ENTRY_LEN 16 +// Frequency digits +#define FREQ_DIGITS 8 +// Time & Date digits +#define TIMEDATE_DIGITS 10 enum uiScreen { @@ -44,6 +53,79 @@ enum uiScreen LOW_BAT }; +enum SetRxTx +{ + SET_RX = 0, + SET_TX +}; + +/** + * Struct containing a set of positions and sizes that get + * calculated for the selected display size. + * Using these parameters make the UI automatically adapt + * To displays of different sizes + */ +typedef struct layout_t +{ + uint16_t hline_h; + uint16_t top_h; + uint16_t line1_h; + uint16_t line2_h; + uint16_t line3_h; + uint16_t bottom_h; + uint16_t status_v_pad; + uint16_t line_v_pad; + uint16_t horizontal_pad; + uint16_t text_v_offset; + point_t top_left; + point_t line1_left; + point_t line2_left; + point_t line3_left; + point_t bottom_left; + point_t top_right; + point_t line1_right; + point_t line2_right; + point_t line3_right; + point_t bottom_right; + fontSize_t top_font; + fontSize_t line1_font; + fontSize_t line2_font; + fontSize_t line3_font; + fontSize_t bottom_font; +} layout_t; + +/** + * This structs contains state variables internal to the + * UI that need to be kept between executions of the UI + * This state does not need to be saved on device poweroff + */ +typedef struct ui_state_t +{ + uint8_t menu_selected; + uint8_t input_number; + uint8_t input_position; + uint8_t input_set; + freq_t new_rx_frequency; + freq_t new_tx_frequency; + char new_rx_freq_buf[14]; + char new_tx_freq_buf[14]; +#ifdef HAS_RTC + curTime_t new_timedate; + char new_date_buf[9]; + char new_time_buf[9]; +#endif +} ui_state_t; + +extern layout_t layout; +extern const char *menu_items[6]; +extern const char *settings_items[1]; +extern const uint8_t menu_num; +extern const uint8_t settings_num; +extern const color_t color_black; +extern const color_t color_grey; +extern const color_t color_white; +extern const color_t yellow_fab413; + /** * This function initialises the User Interface, starting the * Finite State Machine describing the user interaction. diff --git a/openrtx/include/ui_menu.h b/openrtx/include/ui_menu.h new file mode 100644 index 00000000..e1139f7c --- /dev/null +++ b/openrtx/include/ui_menu.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN, * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#ifndef UI_MENU_H +#define UI_MENU_H + +#include + +void _ui_drawMenuList(point_t pos, const char *entries[], + uint8_t num_entries, uint8_t selected); +void _ui_drawChannelList(point_t pos, uint8_t selected); +void _ui_drawMenuTop(ui_state_t* ui_state); +void _ui_drawMenuChannel(ui_state_t* ui_state); +void _ui_drawMenuSettings(ui_state_t* ui_state); +#ifdef HAS_RTC +void _ui_drawSettingsTimeDate(state_t* last_state, ui_state_t* ui_state); +void _ui_drawSettingsTimeDateSet(state_t* last_state, ui_state_t* ui_state); +#endif +bool _ui_drawMenuMacro(state_t* last_state); + +#endif /* UI_MENU_H */ diff --git a/openrtx/src/ui.c b/openrtx/src/ui.c index 91e9ae44..b55ee2aa 100644 --- a/openrtx/src/ui.c +++ b/openrtx/src/ui.c @@ -66,24 +66,14 @@ #include #include #include +#include #include -#include -#include -#include #include #include -#include #include #include #include -// Maximum menu entry length -#define MAX_ENTRY_LEN 16 -// Frequency digits -#define FREQ_DIGITS 8 -// Time & Date digits -#define TIMEDATE_DIGITS 10 - const char *menu_items[6] = { "Zone", @@ -93,8 +83,6 @@ const char *menu_items[6] = "GPS", "Settings" }; -// Calculate number of main menu entries -const uint8_t menu_num = sizeof(menu_items)/sizeof(menu_items[0]); const char *settings_items[1] = { @@ -102,77 +90,17 @@ const char *settings_items[1] = "Time & Date" #endif }; + +// Calculate number of main menu entries +const uint8_t menu_num = sizeof(menu_items)/sizeof(menu_items[0]); // Calculate number of settings menu entries const uint8_t settings_num = sizeof(settings_items)/sizeof(settings_items[0]); -/** - * Struct containing a set of positions and sizes that get - * calculated for the selected display size. - * Using these parameters make the UI automatically adapt - * To displays of different sizes - */ -typedef struct layout_t -{ - uint16_t hline_h; - uint16_t top_h; - uint16_t line1_h; - uint16_t line2_h; - uint16_t line3_h; - uint16_t bottom_h; - uint16_t status_v_pad; - uint16_t line_v_pad; - uint16_t horizontal_pad; - uint16_t text_v_offset; - point_t top_left; - point_t line1_left; - point_t line2_left; - point_t line3_left; - point_t bottom_left; - point_t top_right; - point_t line1_right; - point_t line2_right; - point_t line3_right; - point_t bottom_right; - fontSize_t top_font; - fontSize_t line1_font; - fontSize_t line2_font; - fontSize_t line3_font; - fontSize_t bottom_font; -} layout_t; - -/** - * This structs contains state variables internal to the - * UI that need to be kept between executions of the UI - * This state does not need to be saved on device poweroff - */ -typedef struct ui_state_t -{ - uint8_t menu_selected; - uint8_t input_number; - uint8_t input_position; - uint8_t input_set; - freq_t new_rx_frequency; - freq_t new_tx_frequency; - char new_rx_freq_buf[14]; - char new_tx_freq_buf[14]; -#ifdef HAS_RTC - curTime_t new_timedate; - char new_date_buf[9]; - char new_time_buf[9]; -#endif -} ui_state_t; - const color_t color_black = {0, 0, 0, 255}; const color_t color_grey = {60, 60, 60, 255}; const color_t color_white = {255, 255, 255, 255}; const color_t yellow_fab413 = {250, 180, 19, 255}; -enum SetRxTx -{ - SET_RX = 0, - SET_TX -}; - layout_t layout; ui_state_t ui_state; bool layout_ready = false; @@ -429,64 +357,6 @@ void _ui_drawVFOBottom() TEXT_ALIGN_CENTER, color_white); } -void _ui_drawMenuList(point_t pos, const char *entries[], - uint8_t num_entries, uint8_t selected) -{ - // Number of menu entries that fit in the screen height - uint8_t entries_in_screen = ((SCREEN_HEIGHT - pos.y) / layout.top_h) + 1; - uint8_t scroll = 0; - char entry_buf[MAX_ENTRY_LEN] = ""; - for(int item=0; (item < num_entries) && (pos.y < SCREEN_HEIGHT); item++) - { - // If selection is off the screen, scroll screen - if(selected >= entries_in_screen) - scroll = selected - entries_in_screen + 1; - snprintf(entry_buf, sizeof(entry_buf), "%s", entries[item + scroll]); - if(item + scroll == selected) - { - // Draw rectangle under selected item, compensating for text height - point_t rect_pos = {0, pos.y - layout.top_h + (layout.text_v_offset*2)}; - gfx_drawRect(rect_pos, SCREEN_WIDTH, layout.top_h, color_white, true); - gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_black); - } - else - { - gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_white); - } - pos.y += layout.top_h; - } -} - -void _ui_drawChannelList(point_t pos, uint8_t selected) -{ - // Number of menu entries that fit in the screen height - uint8_t entries_in_screen = ((SCREEN_HEIGHT - pos.y) / layout.top_h) + 1; - uint8_t scroll = 0; - char entry_buf[MAX_ENTRY_LEN] = ""; - int result = 0; - for(int item=0; (result == 0) && (pos.y < SCREEN_HEIGHT); item++) - { - channel_t channel; - result = nvm_readChannelData(&channel, item + scroll); - // If selection is off the screen, scroll screen - if(selected >= entries_in_screen) - scroll = selected - entries_in_screen + 1; - snprintf(entry_buf, sizeof(entry_buf), "%s", channel.name); - if(item + scroll == selected) - { - // Draw rectangle under selected item, compensating for text height - point_t rect_pos = {0, pos.y - layout.top_h + 3}; - gfx_drawRect(rect_pos, SCREEN_WIDTH, layout.top_h, color_white, true); - gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_black); - } - else - { - gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_white); - } - pos.y += layout.top_h; - } -} - void _ui_drawVFOMain(state_t* last_state) { gfx_clearScreen(); @@ -505,97 +375,6 @@ void _ui_drawVFOInput(state_t* last_state) _ui_drawVFOBottom(); } -void _ui_drawMenuTop(ui_state_t* ui_state) -{ - gfx_clearScreen(); - // Print "Menu" on top bar - gfx_print(layout.top_left, "Menu", layout.top_font, - TEXT_ALIGN_CENTER, color_white); - // Print menu entries - _ui_drawMenuList(layout.line1_left, menu_items, menu_num, ui_state->menu_selected); -} - -void _ui_drawMenuChannel(ui_state_t* ui_state) -{ - gfx_clearScreen(); - // Print "Channel" on top bar - gfx_print(layout.top_left, "Channel", layout.top_font, - TEXT_ALIGN_CENTER, color_white); - // Print channel entries - _ui_drawChannelList(layout.line1_left, ui_state->menu_selected); -} - -void _ui_drawMenuSettings(ui_state_t* ui_state) -{ - gfx_clearScreen(); - // Print "Settings" on top bar - gfx_print(layout.top_left, "Settings", layout.top_font, - TEXT_ALIGN_CENTER, color_white); - // Print menu entries - _ui_drawMenuList(layout.line1_left, settings_items, settings_num, ui_state->menu_selected); -} - -#ifdef HAS_RTC -void _ui_drawSettingsTimeDate(state_t* last_state, ui_state_t* ui_state) -{ - gfx_clearScreen(); - // Print "Time&Date" on top bar - gfx_print(layout.top_left, "Time&Date", layout.top_font, - TEXT_ALIGN_CENTER, color_white); - // Print current time and date - char date_buf[9] = ""; - char time_buf[9] = ""; - snprintf(date_buf, sizeof(date_buf), "%02d/%02d/%02d", - last_state->time.date, last_state->time.month, last_state->time.year); - snprintf(time_buf, sizeof(time_buf), "%02d:%02d:%02d", - last_state->time.hour, last_state->time.minute, last_state->time.second); - gfx_print(layout.line2_left, date_buf, layout.line2_font, TEXT_ALIGN_CENTER, - color_white); - gfx_print(layout.line3_left, time_buf, layout.line3_font, TEXT_ALIGN_CENTER, - color_white); -} - -void _ui_drawSettingsTimeDateSet(state_t* last_state, ui_state_t* ui_state) -{ - (void) last_state; - - gfx_clearScreen(); - // Print "Time&Date" on top bar - gfx_print(layout.top_left, "Time&Date", layout.top_font, - TEXT_ALIGN_CENTER, color_white); - if(ui_state->input_position <= 0) - { - strcpy(ui_state->new_date_buf, "__/__/__"); - strcpy(ui_state->new_time_buf, "__:__:00"); - } - else - { - char input_char = ui_state->input_number + '0'; - // Insert date digit - if(ui_state->input_position <= 6) - { - uint8_t pos = ui_state->input_position -1; - // Skip "/" - if(ui_state->input_position > 2) pos += 1; - if(ui_state->input_position > 4) pos += 1; - ui_state->new_date_buf[pos] = input_char; - } - // Insert time digit - else - { - uint8_t pos = ui_state->input_position -7; - // Skip ":" - if(ui_state->input_position > 8) pos += 1; - ui_state->new_time_buf[pos] = input_char; - } - } - gfx_print(layout.line2_left, ui_state->new_date_buf, layout.line2_font, TEXT_ALIGN_CENTER, - color_white); - gfx_print(layout.line3_left, ui_state->new_time_buf, layout.line3_font, TEXT_ALIGN_CENTER, - color_white); -} -#endif - void ui_init() { redraw_needed = true; @@ -713,81 +492,6 @@ bool _ui_freq_check_limits(freq_t freq) return valid; } -bool _ui_drawMenuMacro(state_t* last_state) { - // Header - gfx_print(layout.top_left, "Macro Menu", layout.top_font, TEXT_ALIGN_CENTER, - color_white); - // First row - gfx_print(layout.line1_left, "1", layout.top_font, TEXT_ALIGN_LEFT, - yellow_fab413); - char code_str[11] = { 0 }; - snprintf(code_str, 11, " %6.1f", - ctcss_tone[last_state->channel.fm.txTone]/10.0f); - gfx_print(layout.line1_left, code_str, layout.top_font, TEXT_ALIGN_LEFT, - color_white); - gfx_print(layout.line1_left, "2 ", layout.top_font, TEXT_ALIGN_CENTER, - yellow_fab413); - char encdec_str[9] = { 0 }; - bool tone_tx_enable = last_state->channel.fm.txToneEn; - bool tone_rx_enable = last_state->channel.fm.rxToneEn; - if (tone_tx_enable && tone_rx_enable) - snprintf(encdec_str, 9, " E+D"); - else if (tone_tx_enable && !tone_rx_enable) - snprintf(encdec_str, 9, " E "); - else if (!tone_tx_enable && tone_rx_enable) - snprintf(encdec_str, 9, " D "); - else - snprintf(encdec_str, 9, " "); - gfx_print(layout.line1_left, encdec_str, layout.top_font, TEXT_ALIGN_CENTER, - color_white); - gfx_print(layout.line1_right, "3 ", layout.top_font, TEXT_ALIGN_RIGHT, - yellow_fab413); - char pow_str[5] = { 0 }; - snprintf(pow_str, 5, "%.1gW", last_state->channel.power); - gfx_print(layout.line1_right, pow_str, layout.top_font, TEXT_ALIGN_RIGHT, - color_white); - // Second row - gfx_print(layout.line2_left, "4", layout.top_font, TEXT_ALIGN_LEFT, - yellow_fab413); - char bw_str[8] = { 0 }; - switch (last_state->channel.bandwidth) - { - case BW_12_5: - snprintf(bw_str, 8, " 12.5"); - break; - case BW_20: - snprintf(bw_str, 8, " 20"); - break; - case BW_25: - snprintf(bw_str, 8, " 25"); - break; - } - gfx_print(layout.line2_left, bw_str, layout.top_font, TEXT_ALIGN_LEFT, - color_white); - gfx_print(layout.line2_left, "5 ", layout.top_font, TEXT_ALIGN_CENTER, - yellow_fab413); - gfx_print(layout.line2_left, " GPS", layout.top_font, TEXT_ALIGN_CENTER, - color_white); - gfx_print(layout.line2_right, "6 ", layout.top_font, TEXT_ALIGN_RIGHT, - yellow_fab413); - gfx_print(layout.line2_right, "Lck", layout.top_font, TEXT_ALIGN_RIGHT, - color_white); - // Third row - gfx_print(layout.line3_left, "7", layout.top_font, TEXT_ALIGN_LEFT, - yellow_fab413); - gfx_print(layout.line3_left, " B+", layout.top_font, TEXT_ALIGN_LEFT, - color_white); - gfx_print(layout.line3_left, "8 ", layout.top_font, TEXT_ALIGN_CENTER, - yellow_fab413); - gfx_print(layout.line3_left, " B-", layout.top_font, TEXT_ALIGN_CENTER, - color_white); - gfx_print(layout.line3_right, "9 ", layout.top_font, TEXT_ALIGN_RIGHT, - yellow_fab413); - gfx_print(layout.line3_right, "Sav", layout.top_font, TEXT_ALIGN_RIGHT, - color_white); - return true; -} - bool _ui_drawDarkOverlay() { // TODO: Make this 245 alpha and fix alpha frame swap color_t alpha_grey = {0, 0, 0, 255}; diff --git a/openrtx/src/ui_menu.c b/openrtx/src/ui_menu.c new file mode 100644 index 00000000..f5710fc6 --- /dev/null +++ b/openrtx/src/ui_menu.c @@ -0,0 +1,249 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +void _ui_drawMenuList(point_t pos, const char *entries[], + uint8_t num_entries, uint8_t selected) +{ + // Number of menu entries that fit in the screen height + uint8_t entries_in_screen = ((SCREEN_HEIGHT - pos.y) / layout.top_h) + 1; + uint8_t scroll = 0; + char entry_buf[MAX_ENTRY_LEN] = ""; + for(int item=0; (item < num_entries) && (pos.y < SCREEN_HEIGHT); item++) + { + // If selection is off the screen, scroll screen + if(selected >= entries_in_screen) + scroll = selected - entries_in_screen + 1; + snprintf(entry_buf, sizeof(entry_buf), "%s", entries[item + scroll]); + if(item + scroll == selected) + { + // Draw rectangle under selected item, compensating for text height + point_t rect_pos = {0, pos.y - layout.top_h + (layout.text_v_offset*2)}; + gfx_drawRect(rect_pos, SCREEN_WIDTH, layout.top_h, color_white, true); + gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_black); + } + else + { + gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_white); + } + pos.y += layout.top_h; + } +} + +void _ui_drawChannelList(point_t pos, uint8_t selected) +{ + // Number of menu entries that fit in the screen height + uint8_t entries_in_screen = ((SCREEN_HEIGHT - pos.y) / layout.top_h) + 1; + uint8_t scroll = 0; + char entry_buf[MAX_ENTRY_LEN] = ""; + int result = 0; + for(int item=0; (result == 0) && (pos.y < SCREEN_HEIGHT); item++) + { + channel_t channel; + result = nvm_readChannelData(&channel, item + scroll); + // If selection is off the screen, scroll screen + if(selected >= entries_in_screen) + scroll = selected - entries_in_screen + 1; + snprintf(entry_buf, sizeof(entry_buf), "%s", channel.name); + if(item + scroll == selected) + { + // Draw rectangle under selected item, compensating for text height + point_t rect_pos = {0, pos.y - layout.top_h + 3}; + gfx_drawRect(rect_pos, SCREEN_WIDTH, layout.top_h, color_white, true); + gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_black); + } + else + { + gfx_print(pos, entry_buf, layout.top_font, TEXT_ALIGN_LEFT, color_white); + } + pos.y += layout.top_h; + } +} + +void _ui_drawMenuTop(ui_state_t* ui_state) +{ + gfx_clearScreen(); + // Print "Menu" on top bar + gfx_print(layout.top_left, "Menu", layout.top_font, + TEXT_ALIGN_CENTER, color_white); + // Print menu entries + _ui_drawMenuList(layout.line1_left, menu_items, menu_num, ui_state->menu_selected); +} + +void _ui_drawMenuChannel(ui_state_t* ui_state) +{ + gfx_clearScreen(); + // Print "Channel" on top bar + gfx_print(layout.top_left, "Channel", layout.top_font, + TEXT_ALIGN_CENTER, color_white); + // Print channel entries + _ui_drawChannelList(layout.line1_left, ui_state->menu_selected); +} + +void _ui_drawMenuSettings(ui_state_t* ui_state) +{ + gfx_clearScreen(); + // Print "Settings" on top bar + gfx_print(layout.top_left, "Settings", layout.top_font, + TEXT_ALIGN_CENTER, color_white); + // Print menu entries + _ui_drawMenuList(layout.line1_left, settings_items, settings_num, ui_state->menu_selected); +} + +#ifdef HAS_RTC +void _ui_drawSettingsTimeDate(state_t* last_state, ui_state_t* ui_state) +{ + gfx_clearScreen(); + // Print "Time&Date" on top bar + gfx_print(layout.top_left, "Time&Date", layout.top_font, + TEXT_ALIGN_CENTER, color_white); + // Print current time and date + char date_buf[9] = ""; + char time_buf[9] = ""; + snprintf(date_buf, sizeof(date_buf), "%02d/%02d/%02d", + last_state->time.date, last_state->time.month, last_state->time.year); + snprintf(time_buf, sizeof(time_buf), "%02d:%02d:%02d", + last_state->time.hour, last_state->time.minute, last_state->time.second); + gfx_print(layout.line2_left, date_buf, layout.line2_font, TEXT_ALIGN_CENTER, + color_white); + gfx_print(layout.line3_left, time_buf, layout.line3_font, TEXT_ALIGN_CENTER, + color_white); +} + +void _ui_drawSettingsTimeDateSet(state_t* last_state, ui_state_t* ui_state) +{ + (void) last_state; + + gfx_clearScreen(); + // Print "Time&Date" on top bar + gfx_print(layout.top_left, "Time&Date", layout.top_font, + TEXT_ALIGN_CENTER, color_white); + if(ui_state->input_position <= 0) + { + strcpy(ui_state->new_date_buf, "__/__/__"); + strcpy(ui_state->new_time_buf, "__:__:00"); + } + else + { + char input_char = ui_state->input_number + '0'; + // Insert date digit + if(ui_state->input_position <= 6) + { + uint8_t pos = ui_state->input_position -1; + // Skip "/" + if(ui_state->input_position > 2) pos += 1; + if(ui_state->input_position > 4) pos += 1; + ui_state->new_date_buf[pos] = input_char; + } + // Insert time digit + else + { + uint8_t pos = ui_state->input_position -7; + // Skip ":" + if(ui_state->input_position > 8) pos += 1; + ui_state->new_time_buf[pos] = input_char; + } + } + gfx_print(layout.line2_left, ui_state->new_date_buf, layout.line2_font, TEXT_ALIGN_CENTER, + color_white); + gfx_print(layout.line3_left, ui_state->new_time_buf, layout.line3_font, TEXT_ALIGN_CENTER, + color_white); +} +#endif + +bool _ui_drawMenuMacro(state_t* last_state) { + // Header + gfx_print(layout.top_left, "Macro Menu", layout.top_font, TEXT_ALIGN_CENTER, + color_white); + // First row + gfx_print(layout.line1_left, "1", layout.top_font, TEXT_ALIGN_LEFT, + yellow_fab413); + char code_str[11] = { 0 }; + snprintf(code_str, 11, " %6.1f", + ctcss_tone[last_state->channel.fm.txTone]/10.0f); + gfx_print(layout.line1_left, code_str, layout.top_font, TEXT_ALIGN_LEFT, + color_white); + gfx_print(layout.line1_left, "2 ", layout.top_font, TEXT_ALIGN_CENTER, + yellow_fab413); + char encdec_str[9] = { 0 }; + bool tone_tx_enable = last_state->channel.fm.txToneEn; + bool tone_rx_enable = last_state->channel.fm.rxToneEn; + if (tone_tx_enable && tone_rx_enable) + snprintf(encdec_str, 9, " E+D"); + else if (tone_tx_enable && !tone_rx_enable) + snprintf(encdec_str, 9, " E "); + else if (!tone_tx_enable && tone_rx_enable) + snprintf(encdec_str, 9, " D "); + else + snprintf(encdec_str, 9, " "); + gfx_print(layout.line1_left, encdec_str, layout.top_font, TEXT_ALIGN_CENTER, + color_white); + gfx_print(layout.line1_right, "3 ", layout.top_font, TEXT_ALIGN_RIGHT, + yellow_fab413); + char pow_str[5] = { 0 }; + snprintf(pow_str, 5, "%.1gW", last_state->channel.power); + gfx_print(layout.line1_right, pow_str, layout.top_font, TEXT_ALIGN_RIGHT, + color_white); + // Second row + gfx_print(layout.line2_left, "4", layout.top_font, TEXT_ALIGN_LEFT, + yellow_fab413); + char bw_str[8] = { 0 }; + switch (last_state->channel.bandwidth) + { + case BW_12_5: + snprintf(bw_str, 8, " 12.5"); + break; + case BW_20: + snprintf(bw_str, 8, " 20"); + break; + case BW_25: + snprintf(bw_str, 8, " 25"); + break; + } + gfx_print(layout.line2_left, bw_str, layout.top_font, TEXT_ALIGN_LEFT, + color_white); + gfx_print(layout.line2_left, "5 ", layout.top_font, TEXT_ALIGN_CENTER, + yellow_fab413); + gfx_print(layout.line2_left, " GPS", layout.top_font, TEXT_ALIGN_CENTER, + color_white); + gfx_print(layout.line2_right, "6 ", layout.top_font, TEXT_ALIGN_RIGHT, + yellow_fab413); + gfx_print(layout.line2_right, "Lck", layout.top_font, TEXT_ALIGN_RIGHT, + color_white); + // Third row + gfx_print(layout.line3_left, "7", layout.top_font, TEXT_ALIGN_LEFT, + yellow_fab413); + gfx_print(layout.line3_left, " B+", layout.top_font, TEXT_ALIGN_LEFT, + color_white); + gfx_print(layout.line3_left, "8 ", layout.top_font, TEXT_ALIGN_CENTER, + yellow_fab413); + gfx_print(layout.line3_left, " B-", layout.top_font, TEXT_ALIGN_CENTER, + color_white); + gfx_print(layout.line3_right, "9 ", layout.top_font, TEXT_ALIGN_RIGHT, + yellow_fab413); + gfx_print(layout.line3_right, "Sav", layout.top_font, TEXT_ALIGN_RIGHT, + color_white); + return true; +}