diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index c752700f5..6ae2d0c47 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1154,7 +1154,11 @@ public: #define UI_COMPACT_MESSAGE_STATUS 0 #endif #ifndef UI_MESSAGE_CHANNEL_FOOTER - #define UI_MESSAGE_CHANNEL_FOOTER 1 + #if defined(MESHCORE_HAS_SMALL_DISPLAY) + #define UI_MESSAGE_CHANNEL_FOOTER 0 + #else + #define UI_MESSAGE_CHANNEL_FOOTER 1 + #endif #endif class MsgPreviewScreen : public UIScreen { diff --git a/src/helpers/ui/DisplayBuildFlags.h b/src/helpers/ui/DisplayBuildFlags.h index 6b6128c65..b58e2fa7d 100644 --- a/src/helpers/ui/DisplayBuildFlags.h +++ b/src/helpers/ui/DisplayBuildFlags.h @@ -4,6 +4,10 @@ // Convert it to a boolean that shared ESP32 code can use without including a // concrete driver. Undefined identifiers evaluate to zero in a #if expression. #define MESHCORE_NULL_DISPLAY_CLASS_NullDisplayDriver 1 +#define MESHCORE_SMALL_DISPLAY_CLASS_SH1106Display 1 +#define MESHCORE_SMALL_DISPLAY_CLASS_SSD1306Display 1 +#define MESHCORE_SMALL_DISPLAY_CLASS_ST7735Display 1 +#define MESHCORE_SMALL_DISPLAY_CLASS_U8g2Display 1 #define MESHCORE_DISPLAY_TOKEN_JOIN_INNER(prefix, name) prefix##name #define MESHCORE_DISPLAY_TOKEN_JOIN(prefix, name) \ MESHCORE_DISPLAY_TOKEN_JOIN_INNER(prefix, name) @@ -13,6 +17,19 @@ #define MESHCORE_HAS_REAL_DISPLAY 1 #endif +// These drivers back physically compact panels where the message-channel +// selector consumes too much of the useful text area. Do not infer this from +// DisplayDriver::height(): larger scaled TFTs intentionally expose the same +// 128x64 logical canvas. +#if defined(DISPLAY_CLASS) \ + && MESHCORE_DISPLAY_TOKEN_JOIN(MESHCORE_SMALL_DISPLAY_CLASS_, DISPLAY_CLASS) + #define MESHCORE_HAS_SMALL_DISPLAY 1 +#endif + #undef MESHCORE_DISPLAY_TOKEN_JOIN #undef MESHCORE_DISPLAY_TOKEN_JOIN_INNER +#undef MESHCORE_SMALL_DISPLAY_CLASS_U8g2Display +#undef MESHCORE_SMALL_DISPLAY_CLASS_ST7735Display +#undef MESHCORE_SMALL_DISPLAY_CLASS_SSD1306Display +#undef MESHCORE_SMALL_DISPLAY_CLASS_SH1106Display #undef MESHCORE_NULL_DISPLAY_CLASS_NullDisplayDriver diff --git a/test/test_t096_message_footer_profile.py b/test/test_t096_message_footer_profile.py index 82601a792..ecb63cafc 100644 --- a/test/test_t096_message_footer_profile.py +++ b/test/test_t096_message_footer_profile.py @@ -1,15 +1,39 @@ #!/usr/bin/env python3 from pathlib import Path +import subprocess import unittest ROOT = Path(__file__).resolve().parents[1] PROFILE = ROOT / "variants" / "heltec_t096" / "platformio.ini" +V4_PROFILE = ROOT / "variants" / "heltec_v4" / "platformio.ini" UI = ROOT / "examples" / "companion_radio" / "ui-new" / "UITask.cpp" +DISPLAY_FLAGS = ROOT / "src" / "helpers" / "ui" / "DisplayBuildFlags.h" class T096MessageFooterProfileTest(unittest.TestCase): + def preprocessor_macros(self, display_class): + result = subprocess.run( + [ + "c++", + "-E", + "-dM", + "-x", + "c++", + f"-DDISPLAY_CLASS={display_class}", + "-include", + str(DISPLAY_FLAGS), + "-", + ], + input="", + text=True, + capture_output=True, + check=False, + ) + self.assertEqual(result.returncode, 0, result.stderr) + return result.stdout + def test_t096_disables_noninteractive_message_channel_footer(self): profile = PROFILE.read_text(encoding="utf-8") base_profile = profile.split("[Heltec_t096]", 1)[1].split( @@ -25,11 +49,40 @@ class T096MessageFooterProfileTest(unittest.TestCase): } self.assertIn("-D UI_MESSAGE_CHANNEL_FOOTER=0", active_flags) - def test_other_ui_new_targets_keep_the_footer_by_default(self): + def test_compact_display_drivers_disable_the_footer_by_default(self): + flags = DISPLAY_FLAGS.read_text(encoding="utf-8") + for display_class in ( + "SH1106Display", + "SSD1306Display", + "ST7735Display", + "U8g2Display", + ): + self.assertIn( + f"#define MESHCORE_SMALL_DISPLAY_CLASS_{display_class} 1", + flags, + ) + for display_class in ( + "E213Display", + "E290Display", + "GxEPDDisplay", + "NV3001BDisplay", + "SCIndicatorDisplay", + "ST7789Display", + "ST7789LCDDisplay", + ): + self.assertNotIn( + f"MESHCORE_SMALL_DISPLAY_CLASS_{display_class}", flags + ) + self.assertIn("#define MESHCORE_HAS_SMALL_DISPLAY 1", flags) + ui = UI.read_text(encoding="utf-8") self.assertIn( "#ifndef UI_MESSAGE_CHANNEL_FOOTER\n" - " #define UI_MESSAGE_CHANNEL_FOOTER 1\n" + " #if defined(MESHCORE_HAS_SMALL_DISPLAY)\n" + " #define UI_MESSAGE_CHANNEL_FOOTER 0\n" + " #else\n" + " #define UI_MESSAGE_CHANNEL_FOOTER 1\n" + " #endif\n" "#endif", ui, ) @@ -46,6 +99,47 @@ class T096MessageFooterProfileTest(unittest.TestCase): self.assertNotIn("display.", disabled) self.assertIn("(void)display;", disabled) + def test_small_display_token_classification_preprocesses_exactly(self): + for display_class in ( + "SH1106Display", + "SSD1306Display", + "ST7735Display", + "U8g2Display", + ): + macros = self.preprocessor_macros(display_class) + self.assertIn("#define MESHCORE_HAS_REAL_DISPLAY 1", macros) + self.assertIn("#define MESHCORE_HAS_SMALL_DISPLAY 1", macros) + + for display_class in ( + "E213Display", + "E290Display", + "GxEPDDisplay", + "NV3001BDisplay", + "SCIndicatorDisplay", + "ST7789Display", + "ST7789LCDDisplay", + ): + macros = self.preprocessor_macros(display_class) + self.assertIn("#define MESHCORE_HAS_REAL_DISPLAY 1", macros) + self.assertNotIn("MESHCORE_HAS_SMALL_DISPLAY", macros) + + null_macros = self.preprocessor_macros("NullDisplayDriver") + self.assertNotIn("MESHCORE_HAS_REAL_DISPLAY", null_macros) + self.assertNotIn("MESHCORE_HAS_SMALL_DISPLAY", null_macros) + + def test_v4_oled_is_compact_but_v4_tft_is_not(self): + profile = V4_PROFILE.read_text(encoding="utf-8") + usb = profile.split( + "[env:heltec_v4_companion_radio_usb_femon]", 1 + )[1].split("[", 1)[0] + tft_usb = profile.split( + "[env:heltec_v4_tft_companion_radio_usb_femon]", 1 + )[1].split("[", 1)[0] + self.assertIn("extends = heltec_v4_oled", usb) + self.assertIn("DISPLAY_CLASS=SSD1306Display", usb) + self.assertIn("extends = heltec_v4_tft", tft_usb) + self.assertIn("DISPLAY_CLASS=ST7789LCDDisplay", tft_usb) + if __name__ == "__main__": unittest.main()